存档

文章标签 ‘duck typing’

浅谈程序语言中的鸭子类型(Duck Typing)

2015年4月13日 没有评论

引子


今天在看Underscore的API文档时遇到了一个术语”Duck Typing”,面向对象的编程思想学习了这么久,竟然不知道这个术语是什么意思,赶紧google了一把脑补一下。

 

起源


wiki的解释来看,这个概念的名字来源于由James Whitcomb Riley提出的鸭子测试,“鸭子测试”可以这样表述:

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

 

阐释


wiki上的定义如下:

在程序设计中,鸭子类型(英语:duck typing)是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由当前方法和属性的集合决定。

wiki上的定义比较绕口,个人的理解就是你不需要去严格地继承自某个类,但是你可以实现该类所具有的一些特征,让这个类即使是鸟,看起来像鸭子就行。

 

例子


个人经常用的动态语言就是Python和Javascript。所以分别找了这两个语言的例子帮助大家进行理解。

 

Python

Python的例子满大街都是,网上抄了一个,如下所示:

#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)

这个例子实在简单,我就不多解释了,大意就是Duck,Bird以及Doge虽然没有继承自同一个父类,但是都具有quack方法。

 

Javascript

Javascript的例子来源于Stackoverflow,问题的回答者写的十分风趣,用流行的话说,就是萌萌哒…

链接如下:http://stackoverflow.com/questions/12762550/example-of-javascript-duck-typing

 

回顾Underscore


在Underscore的文档中,有这么一段话涉及到了Duck Typing:

Note: Collection functions work on arrays, objects, and array-like objects such asargumentsNodeList and similar. But it works by duck-typing, so avoid passing objects with a numeric length property. It’s also good to note that an each loop cannot be broken out of — to break, use _.find instead.

意思就是说underscore的collection相关的函数是由Duck typing的方式来编写的,不能传入带有length属性的对象,否则就不正常工作了。

OK,那么我们再来看下Underscore的源代码,我们找个比较简单的each函数来看下,下面的函数依赖于传入参数obj的length,如果obj是个数组的话,那当然没什么问题,但是如果传入参数是一个{length: 100},那就只能呵呵了。

_.each = _.forEach = function(obj, iteratee, context) {
    if (obj == null) return obj;
    iteratee = createCallback(iteratee, context);
    var i, length = obj.length;
    if (length === +length) {
      for (i = 0; i < length; i++) {
        iteratee(obj[i], i, obj);
      }
    } else {
      var keys = _.keys(obj);
      for (i = 0, length = keys.length; i < length; i++) {
        iteratee(obj[keys[i]], keys[i], obj);
      }
    }
    return obj;
  };