首页 > 编程笔记 > Go语言笔记

Go语言接口实现多态(鸭子类型)

很多编程语言都支持鸭子类型(Duck Typing),通常鸭子类型是动态编程语言用来实现多态的一种方式。


图 1 大黄鸭

理解鸭子类型之前,我们先看上图,它是曾经很受欢迎的大黄鸭,从人们认知的角度分析,它不是一只鸭子,因为它没有生命,更不要说它具备鸭子的本能,但是从鸭子类型角度来看,它就是一只鸭子。鸭子类型的原意是:只要走起来像鸭子,或者游泳姿势像鸭子,或者叫声像鸭子,那么它就是一只鸭子。用官方术语解释:鸭子类型只关心事物的外部行为而非内部结构。

我们知道接口方法必须与结构体进行绑定,每次使用的时候都要创建接口变量、创建结构体实例化变量、结构体与接口绑定等,在使用上造成诸多不便。如果将接口与结构体的绑定过程以函数实现,只要传入结构体实例化变量就能自动执行接口方法,示例如下:
package main

import "fmt"

// 定义接口
type actions interface {
    speak(content string)
}

// 定义结构体
type duck struct {
    name string
}

type cat struct {
    name string
}

// 定义结构体方法
func (d *duck) speak(content string) {
    fmt.Printf("%v在说话:%v\n", d.name, content)
}

func (c *cat) speak(content string) {
    fmt.Printf("%v在说话:%v\n", c.name, content)
}

// 定义函数
func speaking(a actions, content string) {
    a.speak(content)
}

func main() {
    // 实例化结构体
    d := duck{name: "唐老鸭"}
    c := cat{name: "凯蒂猫"}
    // 调用函数
    speaking(&d, "嘎嘎")
    speaking(&c, "喵喵")
}
上述代码的实现过程如下:
运行上述代码,运行结果为:

唐老鸭在说话:嘎嘎
凯蒂猫在说话:喵喵

从示例看到,我们只要实例化某个结构体,然后将结构体实例化变量的内存地址传入函数 speaking() 就能使用接口方法 actions(),这种方式正是 Duck Typing,使用者只需关心如何使用函数 speaking(),无须关心接口和结构体之间的绑定关系以及结构体所代表的客观事物。

我们再通俗一点理解,函数 speaking() 只要传入结构体实例化变量的内存地址就能将结构体视为“鸭子”,无论结构体代表什么事物,它就是一只鸭子,这与指鹿为马颇有几分相似。

总的来说,Duck Typing 是在接口、结构体和结构体方法已定义的前提下,以函数方式封装结构体和接口的绑定操作,外部使用只需传入结构体实例化变量或指针变量就能调用接口中的方法。

推荐阅读