在 Go 中实现多态可以使用接口,当然,如果你不喜欢使用接口,也有其他方式,这篇文章旨在向你展示实现多态的两种方式,如果您需要实现多态性,接口是第一选择。
首先,让我们看看我们想要做什么:
var dog, duck *Animal
dog = NewDog("fido")
duck = NewDuck("donald")
fmt.Println(dog.makeNoise())
// fido says woof!
fmt.Println(duck.makeNoise())
// donald says quack!
在这里,dog
和 duck
属于同一类型(*Animal
)。每个变量都使用不同的构造函数实例化,并且在调用相同的方法 makeNoise
时它们具有不同的行为。
如果我们使用接口,这段代码会是这样的:
package main
import "fmt"
type Animal interface {
makeNoise() string
}
type Dog struct {
name string
legs int
}
func (d *Dog) makeNoise() string {
return d.name + " says woof!"
}
type Duck struct {
name string
legs int
}
func (d *Duck) makeNoise() string {
return d.name + " says quack!"
}
func NewDog(name string) Animal {
return &Dog{
legs: 4,
name: name,
}
}
func NewDuck(name string) Animal {
return &Duck{
legs: 4,
name: name,
}
}
func main() {
var dog, duck Animal
dog = NewDog("fido")
duck = NewDuck("donald")
fmt.Println(dog.makeNoise())
// fido says woof!
fmt.Println(duck.makeNoise())
// donald says quack!
}
通常,以上场景正是我们使用接口的目的,但我们不希望生活如此简单。
让我们看看不使用接口该如何才能做到这一点:
type Animal struct {
makeNoiseFn func(*Animal) string
name string
legs int
}
Animal
结构体包含 name
和 legs
属性,以及一个 makeNoiseFn
属性,它实际上是一个函数,接受一个 Animal
指针并返回一个字符串。
func (a *Animal) makeNoise() string {
return a.makeNoiseFn(a)
}
makeNoise
方法实际上只是一个包装器,它调用相应的动物 makenoiseFn
,提供指向动物本身的指针作为其参数。
func NewDog(name string) *Animal {
return &Animal{
makeNoiseFn: func(a *Animal) string {
return a.name + " says woof!"
},
legs: 4,
name: name,
}
}
func NewDuck(name string) *Animal {
return &Animal{
makeNoiseFn: func(a *Animal) string {
return a.name + " says quack!"
},
legs: 4,
name: name,
}
}
要让相同的类型表现出不同的行为,我们所要做的就是为它的 makeNoiseFn
属性分配一个不同的函数。makeNoise
方法会根据动物是狗还是鸭子秘密调用不同的函数。