Golang 中的多态

在 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!

在这里,dogduck 属于同一类型(*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 结构体包含 namelegs 属性,以及一个 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 方法会根据动物是狗还是鸭子秘密调用不同的函数。


也可以看看


全国大流量卡免费领

19元月租ㆍ超值优惠ㆍ长期套餐ㆍ免费包邮ㆍ官方正品