说道面向对象(OOP)编程, 就不得不提到下面几个概念:
- 抽象
- 封装
- 继承
- 多态
Is Go An Object Oriented Language?
那么问题来了
- Golang是OOP吗?
- 使用Golang如何实现OOP?
一. 抽象和封装
抽象和封装就放在一块说了. 这个其实挺简单. 看一个例子就行了.
type rect struct {
width int
height int
}
func (r *rect) area() int {
return r.width * r.height
}
func main() {
r := rect{width: 10, height: 5}
fmt.Println("area: ", r.area())
}
structclass
2、可见性. 这个遵循Go语法的大小写的特性
*rectreceiverreceiver
func (r *rect) area() int {
return r.width * r.height
}
func (r rect) area() int {
return r.width * r.height
}
这其中有什么区别和联系呢?
简单来说, Receiver可以是值传递, 还是可以是指针, 两者的差别在于, 指针作为Receiver会对实例对象的内容发生操作,而普通类型作为Receiver仅仅是以副本作为操作对象,并不对原实例对象发生操作。
Receiver*rectr.width(*r).width
5、任何类型都可以声明成新的类型, 因为任何类型都可以有方法.
type Interger int
func (i Interger) Add(interger Interger) Interger {
return i + interger
}
6、虽然Interger是从int声明而来, 但是这样用是错误的.
var i Interger = 1
var a int = i //cannot use i (type Interger) as type int in assignment
隐式转换显式声明
上面的例子改成下面的方式就可以了.
var i Interger = 1
var a int = int(i)
二. 继承(Composition)
说道继承,其实在Golang中是没有继承(Extend)这个概念. 因为Golang舍弃掉了像C++, Java的这种传统的、类型驱动的子类。
Go Effictive says:
Go does not provide the typical, type-driven notion of subclassing, but it does have the ability to “borrow” pieces of an implementation by embedding types within a struct or interface.
Composition
Compostion匿名组合(Pseudo is-a)非匿名组合(has-a)
is-ahas-a
1. has-a
package main
import (
"fmt"
)
type Human struct {
name string
age int
phone string
}
type Student struct {
h Human //非匿名字段
school string
}
func (h *Human) SayHi() {
fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}
func (s *Student) SayHi() {
fmt.Printf("Hi student, I am %s you can call me on %s", s.h.name, s.h.phone)
}
func main() {
mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"}
fmt.Println(mark.h.name, mark.h.age, mark.h.phone, mark.school)
mark.h.SayHi()
mark.SayHi()
}
Output
Mark 25 222-222-YYYY MIT
Hi, I am Mark you can call me on 222-222-YYYY
Hi student, I am Mark you can call me on 222-222-YYYY
structstruct
从上面例子可以, Human完全作为Student的一个字段使用. 所以也就谈不上继承的相关问题了.我们也不去重点讨论.
2. is-a(Pseudo)----Embedding
type Human struct {
name string
age int
phone string
}
type Student struct {
Human //匿名字段
school string
}
func (h *Human) SayHi() {
fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}
func main() {
mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"}
fmt.Println(mark.name, mark.age, mark.phone, mark.school)
mark.SayHi()
}
Output
Mark 25 222-222-YYYY MIT
Hi, I am Mark you can call me on 222-222-YYYY
这里要说的有几点:
StudentHuman
fmt.Println("Student age:", mark.age) //输出: Student age: 25
但是, 我们也可以间接访问:
fmt.Println("Student age:", mark.Human.age) //输出: Student age: 25
Studentnamemark.nameStudentname
fmt.Println("Student name:", mark.name) //输出:Student Name: student name
StudentHumanSayHi()
mark.SayHi() // 输出: Hi, I am Mark you can call me on 222-222-YYYY
SayHi()
type Human struct {
name string
age int
phone string
}
type Student struct {
Human //匿名字段
school string
name string
}
func (h *Human) SayHi() {
fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}
func (h *Student) SayHi() {
fmt.Println("Student Sayhi")
}
func main() {
mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT", "student name"}
mark.SayHi()
}
Output
Student Sayhi
Pseudo is-a
匿名组合多态
package main
type A struct{
}
type B struct {
A //B is-a A
}
func save(A) {
//do something
}
func main() {
b := new(B)
save(*b);
}
Output
cannot use *b (type B) as type A in argument to save
还有一个面试题的例子
type People struct{}
func (p *People) ShowA() {
fmt.Println("showA")
p.ShowB()
}
func (p *People) ShowB() {
fmt.Println("showB")
}
type Teacher struct {
People
}
func (t *Teacher) ShowB() {
fmt.Println("teacher showB")
}
func main() {
t := Teacher{}
t.ShowA()
}
输出结果是什么呢?
Output
ShowA
ShowB
Effective Go Says:
There's an important way in which embedding differs from subclassing. When we embed a type, the methods of that type become methods of the outer type, but when they are invoked the receiver of the method is the inner type, not the outer one
TeacherPeopleTeacherShowA()ShowA()ShowBreceiver*People*TeacherembeddingPseudo is-a
4、 "多继承"的问题
package main
import "fmt"
type School struct {
address string
}
func (s *School) Address() {
fmt.Println("School Address:", s.address)
}
type Home struct {
address string
}
func (h *Home) Address() {
fmt.Println("Home Address:", h.address)
}
type Student struct {
School
Home
name string
}
func main() {
mark := Student{School{"aaa"}, Home{"bbbb"}, "cccc"}
fmt.Println(mark)
mark.Address()
fmt.Println(mark.address)
mark.Home.Address()
fmt.Println(mark.Home.address)
}
输出结果:
30: ambiguous selector mark.Address
31: ambiguous selector mark.address
Embedding匿名字段
mark.Home.Address()
Embedding valueEmbedding pointer
package main
import (
"fmt"
)
type Person struct {
name string
}
type Student struct {
*Person
age int
}
type Teacher struct {
Person
age int
}
func main() {
s := Student{&Person{"student"}, 10}
t := Teacher{Person{"teacher"}, 40}
fmt.Println(s, s.name)
fmt.Println(t, t.name)
}
Output
{0x1040c108 10} student
{{teacher} 40} teacher
Embedding valueEmbedding pointer
三. Interface
CompositeGolangInterface
下面是我工程里面一段代码的简化:
package main
import (
"fmt"
)
type Check interface {
CheckOss()
}
type CheckAudio struct {
//something
}
func (c *CheckAudio) CheckOss() {
fmt.Println("CheckAudio do CheckOss")
}
func main() {
checkAudio := CheckAudio{}
var i Check
i = &checkAudio //想一下这里为啥需要&
i.CheckOss()
}
Composite
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
type ReadWriter interface {
Reader
Writer
}
ReaderWriterReadWriterReadWriterReaderWriter
尾声
至此, 基本说完了Golang的面向对象. 有哪里我理解的不对的地方, 请给我留言.
参考资料