分享 Duck Typing

dandananddada · 2015年05月13日 · 最后由 ryan 回复于 2015年05月14日 · 4587 次阅读

什么是 duck typing?

鸭子类型是动态语言的一种风格,这种风格下对象没有有效的语义,是由当前的属性和集合决定。这个概念源自鸭子测试。 当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子 在鸭子类型中,关注的不是对象的类型本身,而是它是如何使用的。 鸭子类型通常得益于不测试方法和函数中参数的类型,而是依赖文档、清晰的代码和测试来确保正确使用。 简单的说鸭子类型关注的不是对象本身,而是方法集的使用(个人感觉和弱类型的用意很像)。

问题

假设你要你要统计一艘货船上有很多货单,每个货单有很多包裹及这些包裹的重量,现在你需要统计这艘船所有货单的包裹总数与总重量,那么怎么做?

# Fields:
# * package_count: number of packages in manifest (integer)
# * mass:          total mass of all packages, in kilograms (BigDecimal)
class ShippingManifest
  attr_accessor :package_count, :mass
end

首先想到的方法

def summarise_manifests
  # Grab the latest set of Manifests (returns an Array)
  manifest_set = ShippingManifest.all

  # Initialize our counters
  total_package_count = 0
  total_mass          = BigDecimal("0")

  manifest_set.each do |m|
    total_package_count += m.package_count
    total_mass += m.mass
  end

  return [total_package_count, total_mass]
end

如何用一行代码?

def summarise_manifests
  ShippingManifest.all.sum
end

Duck typing!

class ShippingManifest < ActiveResource::Base
  # Returns a new instance of ShippingManifest containing the
  # sum of both `mass` and `package_count`
  def +(other)
    self.dup.tap do |neu|
      neu.mass          = @mass + other.mass
      neu.package_count = @package_count + other.package_count
    end
  end
end

引自

Making Ruby Quack—Why We Love Duck Typing http://www.sitepoint.com/making-ruby-quack-why-we-love-duck-typing/

这是重载了加号运算符吗?

#1 楼 @ryan 是。。。其实没太明白这个例子和 duck 的关系,感觉不是很清晰

Duck typing 是一个非常有名的隐喻,这个隐喻我认为是说给那些有强类型语言背景的人听的,因为强类型语言中对象的行为必须符合其类型。

但是很多 Ruby 程序员不一定是从强类型语言转过来的,在学习 Ruby 之前压根就没有类型系统这个根深蒂固的概念,比如我,在初学阶段就不明白这么强调 Duck Typing 是为什么?说简单一点,就是让你把注意力关注在对象的行为能力上,而不是其类型本身。

#2 楼 @dandananddada 原文中解释了这个例子跟 duck type 的关系,就是对一个集合使用 sum 方法,不管集合中的元素分别是什么类型,只要每个元素对象都能响应 inject 的调用并返回数值,就能算出结果。

#4 楼 @lgn21st 我之前也这么理解,但是感觉这个例子并没有让人恍然大悟的感觉,更像一个如何用 ruby 写更少的代码做同样的事的例子。。

#3 楼 @lgn21st 赞,所以这种概念性的东西知道就行,没必要过分强调,因为本身大家都是在这么用的。。。可以这么说?

#2 楼 @dandananddada duck type 和泛型是不是差不多?从 OO 角度来想,能够响应某种行为的对象那一定这个对象所属的类定义此行为,只是这个对象也许和其他定义此行为的对象并非属于同一类。。。我是主做 JAVA 的,第一反映就是属于 duck type 的应该都集成某个接口了。只是继承同个接口的类未必有任何关联。。

#7 楼 @ryan 是啊,可以这么理解,ruby,php 这些弱类型语言,变量声明是不需要绑定到具体类型的,其实就和 Java 用 Objec 声明一样,这里的例子用到的运算符重载,在 java 教材里不也是在泛型的章节讲的。不过使用上确实还是有区别的,就算你把变量作为反省传递进函数了,还是需要对泛型解析绑定到具体对象的,但是弱类型就不存在这种概念了。。。确实会方便很多,但是变量存储上要付出代价。。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号