0%

Duck Typing

最近在看Dynamic programming language的時候,注意到了這個特性。
根據Wiki給的定義

When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck.

一般OOP的觀念,就是定義一組Interface為公約數。不受Interface的約束,而動態語言是在執行時檢查是否滿足所需條件。

以下是Ruby的範例

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
class Duck
def quack
puts "Quaaaaaack!"
end

def feathers
puts "The duck has white and gray feathers."
end
end

class Person
def quack
puts "The person imitates a duck."
end

def feathers
puts "The person takes a feather from the ground and shows it."
end
end

def in_the_forest duck
duck.quack
duck.feathers
end

def game
donald = Duck.new
john = Person.new
in_the_forest donald
in_the_forest john
end

game

動態語言的特性,就是在執行的時刻檢查類別的完整性,如果把上面Person的feathers拿掉,也必須在執行時才會發現錯誤。

如果是傳統競泰語言的話,如何顯示這種特性
C++有兩種方式。

一者是上面介紹的Interface contract

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class IAnimal {
public:
virtual void Quack() = 0;
virtual void Feathers() = 0;
};

class Duck : public IAnimal {};
class Person : public IAnimal {};

void in_the_forest(IAnimal *pDuck)
{
pDuck->Quack();
pDuck->Feathers();
}

這種方式可以在Runtime的時候傳入某個滿足此Contract的物件,因此改變其行為。
不過缺點也是顯而易見的,一旦介面修改之後,所有有關的程式碼都要更著修改。所以一般的建議就是一旦介面固定之後就不要更改。

因此衍生了第二種方法,Template。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Duck {
public:
void Quack();
void Feathers();
};
class Person {
public:
void Quack();
void Feathers();
};
template <typename T>
void Program(T& duck)
{
duck.Quack();
duck.Feathers();
}

C++的Template會在C編譯時檢查所有約束條件,因此叫做Static Polymorphism,跟用Interface的Dynamic Polymorphism不同,Runtime沒有擴展性。