引子
今天在看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 asarguments, NodeList 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;
}; |