go的继承叫做“匿名组合”,这个不等同于组合,也就是说:“匿名组合”是“匿名组合”,“组合”是“组合”,两个完全不一样。下面看一个最简单的组合,然后再比较后面讲到的“匿名组合”,会发现两者不同。
type A struct {
}
func (a *A) Test() {
}
type B struct {
a A
}
func (b *B) Test() {
a.Test()
}
var b = new(B)
b.Test()
从上面可以看到“组合”的特点,要调用A的Test()方法,则需要用B的某个方法给封装一下,有没有一种更便利的方法,不用在B中写一个包装方法来调用,能不能省略掉B中的Test()包装方法呢?且看后面。
go的“匿名组合”,有三种实现形式:
1,匿名组合struct
type Human struct {
Name string
}
func (a *Human) Test1() {}
type Student struct {
Human
Age int
}
func (a *Student) Test2() {}
var a = new(Student)
a.Name = "zhangsan"
a.Age = 11
a.Test2()
a.Test1() == a.Human.Test1() //这2种方式都可以
2,匿名组合指向struct的指针
type Hobby struct {
Name string
}
func (a *Hobby) Test1() {}
type Student struct {
*Hobby
Age int
}
var h = &Hobby{"movie"}
var s = &Student{h,18}
s.Test1()
初看这种方式和第一种匿名组合struct没有什么区别,而且更麻烦了,在创建Student的时候,还要对其中的*Hobby进行赋值。但是细细一想,这种模式的优点很大,这种类型的“匿名组合”实际上是第一种的“匿名组合”stuct再加上”组合“这2种模式而成。
对于第一种”匿名组合“struct,往往用在派生类和父类有一定继承关系上,而对于第二种的”匿名组合“指向struct的指针来说,派生类中的组合对象可以和父类没有太大的关系或者没有任何关系,就是一个纯粹的”组合“模式,上面讲到了,在组合模式中要调用父类的方法,需要在派生类中再写一个包装方法,很是麻烦。刚好,用第二种的”匿名组合“指向struct的指针这种模式来说,非常好的解决了这个问题,非常完美。
上面的例子假如看得还不是特别明白的话,好好体会下面这个例子就非常清楚了,这个模式非常不错:
package main
import (
"fmt"
"log"
"os"
)
type MyJob struct {
Command string
*log.Logger
}
func (job *MyJob) Start() {
job.Println("job started!") // job.Logger.Println
fmt.Println(job.Command)
job.Println("job finished!") // job.Logger.Println
}
func main() {
logFile, err := os.OpenFile("./job.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0)
if err != nil {
fmt.Println("%s", err.Error())
return
}
defer logFile.Close()
logger := log.New(logFile, "[info]", log.Ldate|log.Ltime|log.Llongfile)
job := MyJob{"programming", logger}
job.Start()
job.Println("test finished!") // job.Logger.Println
}
因为 MyJob 内组合的*log.Logger和MyJob关系不是纯粹的继承,假如将 log.Logger 采用组合的方式,那么还需要对 MyJob 再定义一个 Println 或者一系列方法,而采用”匿名组合“指向struct指针的这种方法就有效的解决了这些问题,细细体会。
3,匿名组合接口
这个和第2种模式几乎一样,也需要在实例化派生类的时候,要对其中的匿名组合进行接口实现类的赋值,道理和第2种一样。