likes
comments
collection
share

接口和结构嵌入让Go的多态模型更强大

作者站长头像
站长
· 阅读数 18

Go 中没有 Class ,只有 struct 。这是 Golang 和其他面向对象语言的差异的地方,也是其独特之处。Go 有两个很棒的特性,使其多态模型比经典的继承更加强大——接口和结构嵌入。

接口

Go 中的接口与 Java 接口非常不同。 在 Go 中不需要明确表示数据类型实现了接口,只需要实现接口定义的所有方法,那么就是实现了接口。而在 Java 中需要使用 implement 关键字明确表示实现接口。

例如,让我们这样定义一个 Animal 接口,结构体 Dog 满足 Animal 接口, 因为它具有 Animal 实现的所有方法。

type Animal interface {
 SayHi() string
}

// Dog 实现了接口
type Dog struct {}
func (d *Dog) SayHi() string {
 fmt.Println("i am dog")
 return "hi"
}

func (d *Dog) Bark() {
 fmt.Println(“Woof!”)
}

使用时,将初始化的 Dog 实例赋值给 Animal 类型的 animal 变量,然后调用 animal.SayHi() 方法,就可以正常调用。使用 Dog{} 就会编译失败,因为满足接口的是 *Dog。

func main(){
    var animal Animal
    animal = &Dog{}
    animal.SayHi() // i am dog

    // error use
    animal = Dog{} // 编译将会失败

}

我们还可以使用嵌入的方式定义接口,如下代码所示:

type PAnimal interface {
 Animal
 Party()
}

定义了一个接口 PAnimal,它必须实现 Party() 方法的基础上也要实现 Animal 的所有方法,其实等价于:

type PAnimal interface {
 SayHi() string
 Party()
}

结构嵌入

当我们想创建一个类型,它可以完成其他类型,比如 Dog 所做的所有事情,甚至更多。我们可以这样做:

type FlyDog {
    *Dog
}
func (fd *FlyDog) Fly(h *Human) {
 fd.SayHi()
 fmt.Printf("I can fly")
}


// 使用
func main(){
    fd := &FlyDog{}
    fd.Fly() // i am dog i can fly
}

通过执行结果可知,它会调用 *Dog 的 SayHi 方法。但是如果在结构体内嵌入2个其他的结构体的时候,就需要明确指定哪个结构体。比如还有一个结构体 Cat 和 Dog 一样都嵌入到 FlyDog 内,那使用时

type FlyDog struct {
 *Cat
 *Dog
}

func (fd *FlyDog) Fly(h *Human) {
 fd.Cat.SayHi()
 fd.Dog.SayHi()
 fmt.Printf("I can fly")
}

Go 多态性

Go 多态性涉及创建满足公共接口的许多不同数据类型。 这与 Java 的不同之处在于 Java 多态性还允许满足公共基类,这可能会导致许多问题。

在使用 class 时,我们经常会定义一个基类然后其他实现或者继承基类。但是在维基百科中对基类定义为不安全的。因为底层基类被修改也很可能会导致派生类出现故障。这种方式其实也是一种强耦合的关系。

所以,优先组合而不是继承

继承会产生一个复杂、多层级的结构关系。而通过组合,通过选择不同的特征组合成你想要的。如果是继承就可能需要修改层次结构。

Go 的结构嵌​​入其实就是将组合伪装成继承,并允许通过其强大的接口轻松更改实现细节。它比基于类的继承模型强大得多,因为它允许更大程度的灵活性。

参考资料: