WIKI讲鸭子类型 已经很清晰了,我接触这个概念是在看雨痕大神的《Go语言学习笔记》一书得来的,作者再讲Golang的接口类型说就是Duck type。

图片来源:嘎嘎叫的小狗——快乐的鸭子类型

如下代码实现go的接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package main

import (
"fmt"
)

type User struct {
name string
age byte
}

type Manager struct {
User
title string
}

func (u User) ToString() string {
return fmt.Sprintf("%+v", u)
}

func (m Manager) PrintIt() string {
return "good"
}

// 接口类型
type Print interface {
PrintIt() string
ToString() string
}

func main() {
var admin Manager
admin.name = "方朋"
admin.age = 26
admin.title = "厂长"
fmt.Println(admin.ToString())

// 只要包含接口所需的全部方法,即实现了该接口
var p Print = admin
fmt.Println(p.PrintIt())
fmt.Println(p.ToString())
}

鸭子类型本质体现的是面向接口的编程,对于Go来说,接口是一种契约,是多个方法的集合。就像鸭子一样,有叫、跑、游泳的方法我们说这是鸭子,对于Go,接口实现比较简洁,只要目标类型方法集内包含接口声明的全部方法,就称实现了该接口,不用做显示声明。

对于OOP来说,体现的是面向继承的编程,但”鸭子类型”像多态一样工作,只是没有继承。

“鸭子类型”的语言是这么推断的:走起来像鸭子、游起泳来像鸭子、叫起来也像鸭子,那它就可以被当做鸭子。也就是说,它不关注对象的类型,而是关注对象具有的行为(方法)。

上述就是鸭子类型的表现,也就是任何拥有这样的正确的”走”和”叫”方法的对象都可被函数接受的这种行为。

如下Python代码演示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class Duck(object):
def quack(self):
print("鸭子呱呱叫")

def swim(self):
print("鸭子游泳")


class Person(object):
def quack(self):
print("这人模仿鸭子呱呱叫")

def swim(self):
print("这人模仿鸭子游泳")


def interfaces(duck):
"""
定义一个接口, 约定呱呱叫和游泳的契约
接收一个对象, 看这个对象所拥有的方法存不存在,不存在触发异常
如果都存在, 不管是人还是真鸭子, 都被认为是鸭子
:param duck:
:return:
"""
duck.quack() # 呱呱叫
duck.swim() # 游泳


def game():
donald = Duck() # 唐老鸭
jack = Person() # Jack
interfaces(donald)
interfaces(jack)

game()
# out print:
# 鸭子呱呱叫
# 鸭子游泳
# 这人模仿鸭子呱呱叫
# 这人模仿鸭子游泳

在Python中,鸭子类型的最典型例子就是类似file(file-like)的类。这些类可以实现file的一些或全部方法,并可以用于file通常使用的地方。例如,GzipFile,cStringIO,套接字(socket)也和文件共同拥有许多相同的方法。然而套接字缺少tell()方法,不能用于GzipFile可以使用的所有地方。这体现了鸭子类型的可伸缩性:一个类似file的对象可以实现它有能力实现的方法,且只能被用于它有意义的情形下。

“鸭子类型”没有任何静态检查,如类型检查、属性检查、方法签名检查等。,一切都靠契约和自觉性,所以在可能会在运行时因为不具备某种特定的方法而抛出异常。

Duck-typing avoids tests using type() or isinstance(). Instead, it typically employs the EAFP (Easier to Ask Forgiveness than Permission) style of programming.

在Python里EAFP原则描述了异常处理的使用。例如相对于检查一个自称为类似Duck的对象是否拥有一个quack()方法(使用if hasattr(mallard, "quack"): ...),人们通常更倾向于用异常处理把对quack的调用尝试包裹起来:

1
2
3
4
try:
mallard.quack()
except (AttributeError, TypeError):
print "mallard并沒有quack()函式"

在Js里和Python一样,都属于动态语言,比较灵活。如下是JS的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function guagua(duck) {
duck.quack("呱呱叫..");
}

var donald = {quack: function(msg) { console.log(msg) }};
var jack = {name:"Jack",
swim: function(e){},
quack: function(msg) { console.log(msg) }
};

> guagua(donald)
呱呱叫..

> guagua(jack)
呱呱叫..

完~