hello云胜

技术与生活

0%

继承

首先 Go 不支持经典的面向对象编程范式,但是我们也会看到go中存在继承这种说法。其实这里的继承只是习惯性的使用了继承这个词而已,真正表达的意思是一种组合的思想。

这种组合是通过go的类型嵌入实现的。

类型嵌入

类型嵌入指的就是在一个类型的定义中嵌入了其他类型。Go 语言支持两种类型嵌入,分别 是接口类型的类型嵌入和结构体类型的类型嵌入。

接口类型的类型嵌入

go中的接口定义

1
2
3
4
5
// 定义接口
type Programmer interface {
WriteHello() string
Dance() string
}

定义了一个接口Programmer,他有两个方法WriteHello和Dance。如果某个类型实现了这两个方法,我们就说这个类型实现了接口Programmer。

如果我们再定义一个接口

1
2
3
4
5
type GoProgrammer interface {
WriteHello() string
Dance() string
WriteK8S() string
}

接口GoProgrammer的方法集合中也有WriteHello和Dance,那么就可以用接口Programmer替换这两个方法。

1
2
3
4
type GoProgrammer interface {
Programmer
WriteK8S() string
}

这就是接口类型的类型嵌入。

并且这两种写法的GoProgrammer是等价的。也就是说方法的声明会组合到一起。

这种写法有什么好处呢?

按 Go 语言惯例,Go 中的接口类型中只包含少量方法,并且常常只是一个方法。通过在接 口类型中嵌入其他接口类型可以实现接口的组合,这也是 Go 语言中基于已有接口类型构建新接口类型的惯用法。

实这也是 Go 组合设计哲学的一种体现。

结构体类型的类型嵌入

我们之前讲结构体的时候举过一个例子

1
2
3
4
5
6
7
8
9
type Reader struct {
ReaderName string
Age int
}

type Book struct {
BookName string
Reader
}

Book中的Reader就是一种结构体类型的类型嵌入

这种情况下,Reader的方法会被提升为Book的方法,成为Book方法集合的一部分。(之前的例子是属性,方法也是一样的原理)

1
2
3
4
reader := Reader{"yunsheng", 20}
book := Book{"禅与摩托车维修艺术", reader}
t.Log(book.Reader.ReaderName)
t.Log(book.ReaderName)

可以省略嵌入的Reader字段,而直接访问ReaderName

这也是go语言组合设计思想的一种体现。更具体点说是组合中的代理模式。由Book的ReaderName代理了Book.Reader.ReaderName

如果Book和嵌入的Reader有同名的方法,通过book调用时,先查找Book是否有该方法,没有再查找嵌入的字段有没有该方法。