Duck Typing.
If it walks like a duck and quacks like a duck, then it's a duck.
In Ruby, duck typing refers to the idea that the class of an object (e.g., “Duck”) passed to a method is not important so long as the object can repond to all the messages it is expected to (e.g., “walk to x” or “quack now”). For example:
1 | def mirror_update pt |
当我们看到这个方法时,很自然的认为其应该作用于某个 Point 类的对象,并对该对象的 @x 域取相反数 (并且该类定义了 @x 域的 getter/setter)。但我们发现该方法并没有对参数的类型进行检查 (例如使用 Point? 方法),因此 duck typing is applicable。以下是不同 level 的 duck typing:
- 若
foo类定义了实例变量@x与其对应的 getter/setter,那么foo类的对象也能作为参数传入方法。 - 若
bar类未定义实例变量@x,但其定义了返回值为数字的方法x与x=,那么bar类的对象也能作为参数传入方法。(甚至x方法不需要返回数字,它只需要回应以-1为参数的*信息即可)
因此, Point 类的对象即为 duck objects,而 foo 类对象或 bar 类对象为 duck-like objects。foo 与 bar 类对象虽然不是鸭子,但却能像鸭子一样走 (方法 x) 或叫 (方法 x=)。更经典的例子是某个类与其的子类 (subclass):一般来说,子类对象可以作为 duck-like objects 传入期待父类对象的方法。
Duck typing 能够提高代码的复用性 (reusability),使得 duck-like objects 也能作为 duck objects 作为参数传入方法当中。在 Ruby 中 duck typing 的实现非常简单:
In Ruby, duck typing basically comes for free as long you do not explicitly check that arguments are instances of particular classes using methods like instance_of? or is_a?
当然,duck typing 也有弊端。在该风格中,一个对象有效的语义,并不是继承自特定的类或实现特定的接口,而是由当前方法和属性的集合决定。也就是说,一个“走起来像鸭子”并且“叫起来像鸭子”的对象,可以是鸭子,也可以是类似鸭子的物种,但也可以是一只正在模仿鸭子的龙。但我们“并不总是想让龙进入池塘”。
因此在使用 duck typing 时,程序员必须很好的理解他正在编写的代码。