鸭子类型——Go语言中的多态实现

930 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第8天,点击查看活动详情

“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子."

Duck typing,鸭子类型,是动态编程语言的一种对象推断策略,它更关注对象能如何被使用,而不是对象类型本身。

多态:定义时的类型和运行时的类型不一样。比如动物有多种形态:猫、狗、猪

多态性:不同的类对相同的消息作出不一样的反应

鸭子类型的好处就在于能够避免一些类的重写,无需大量复制相同的代码。前提是需要良好的文档支持,不然会让代码变得很混乱。

go语言

在Go语言中通过接口Interface来实现多态,完美支持鸭子类型。

Go语言不要求类型显示地声明实现某个接口,只要实现了相关的方法即可。Go 接口是一组方法的集合,可以理解为抽象的类型。它提供了一种非侵入式的接口。任何类型,只要实现了该接口中方法集,那么就属于这个类型。比如说实现一个鸭子类型:

先定义一个鸭子叫的接口,以及使用这个接口作为参数的函数

type Duck interface {
  Quack()
}
func DuckSay(d Duck) {
  d.Quack()
}

再来定义两个结构体,实现不同的鸭子叫:

//假设现在有一个可达鸭类型
type PsyDuck struct{}
​
//可达鸭声明方法-满足鸭子会嘎嘎叫的特性
func (pd PsyDuck) Quack() {
   fmt.Println("PsyDuck quack quack")
}
​
//假设现在有一个唐老鸭类型
type DonaldDuck struct{}
​
//唐老鸭声明方法-满足鸭子会嘎嘎叫的特性
func (dd DonaldDuck) Quack() {
   fmt.Println("DonaldDuck gaga")
}

最后在main函数里声明两种不同的鸭子类型,并且调用Quack函数:

func main() {
​
   //实例化对象
   var pd PsyDuck //可达鸭类型
   var dd DonaldDuck //唐老鸭类型
​
   //调用方法 1
   pd.Quack() // 因为可达鸭实现了所有鸭子的函数,所以可以这么用
   dd.Quack() // 因为唐老鸭实现了所有鸭子的函数,所以可以这么用
​
  //调用方法 2
   DuckSay(pd) //因为可达鸭实现了所有鸭子的函数,所以可以这么用
   DuckSay(dd) //因为唐老鸭实现了所有鸭子的函数,所以可以这么用
}

对用调用方法2来说,在main函数中调用Quack函数时,传入的是不同的鸭子对象。编译器会在调用Quack函数的时候,隐式地将pd和dd转换为Duck类型。

总结

鸭子类型是一种动态语言风格。在这种风格中,一个对象有效的语义,不是由特定的类或者是实现特定的接口决定,而是由它当前的方法和属性的集合决定。

参考

Golang的interface及duck typing鸭子类型