1 duck type

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

我们并不关心对象是什么类型,到底是不是鸭子,只关心行为。

duck-type

比如在 python 中,有很多 file-like 的东西,比如 StringIO, GzipFile, socket; 它们有很多相同的方法,我们把它们当作文件使用。

鸭子类型在动态语言中经常使用,非常灵活,使得 python 不像java那样专门去弄一大堆的设计模式。

2 例子

下面举例用duck typing来实现多态:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#coding=utf-8
class Duck:
    def quack(self):
        print "Quaaaaaack!"

class Bird:
    def quack(self):
        print "bird imitate duck."

class Doge:
    def quack(self):
        print "doge imitate duck."

def in_the_forest(duck):
    duck.quack()

duck = Duck()
bird = Bird()
doge = Doge()
for x in [duck, bird, doge]:
    in_the_forest(x)

再举个例子,我们来hack输出流:

1
2
3
4
5
6
7
import sys

sys.stdout = open('stdout.log', 'a') #只要是file-like,不管是什么类型
print 'foo'

sys.stdout = sys.__stdout__ #恢复
print 'bar'

这样就把输出流给写入到文件了。

duck type in 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
package duck_type

import (
    "fmt"
    "testing"
)

type Programmer interface {
    WriteHelloWorld() string
}

type GoProgrammer struct {
}

func (g *GoProgrammer) WriteHelloWorld() string {
    return "fmt.Println(\"Hello World\")"
}

type JavaProgrammer struct {
}

func (j *JavaProgrammer) WriteHelloWorld() string {
    return "System.out.Println(\"Hello World\")"
}

//Duck type entry
func PrintHelloWorld(p Programmer) {
    fmt.Printf("%T, %s\n", p, p.WriteHelloWorld())
}

func TestDuckType(t *testing.T) {
    g := new(GoProgrammer)
    t.Log(g.WriteHelloWorld())

    j := new(JavaProgrammer)
    t.Log(j.WriteHelloWorld())

    //duck type, 入参数只能为Pointer
    PrintHelloWorld(g)
    PrintHelloWorld(j)
}

参考

题图:hongkong-victoria-harbor-duck