background image

 
//Ruby
  5.times {|i| puts i}
  Ruby 中一切皆对象,5 是 Fixnum 类的实例,times 是 Fixnum 的一个方法,它接
受一个 block 参数。相比 for 循环实现,Ruby 的 times 方式更简洁,可读性更强,但熟
悉 OOP 的朋友可能会有疑问,times 是否应该作为整型类的方法呢?在 OOP 中,方法调
用通常代表了向对象发送消息,改变或查询对象的状态,times 方法显然不是对整型对
象状态的查询和修改。如果你是 Ruby 的设计者,你会把 times 方法放入 Fixnum 类吗?
如果答案是否定的,那么 Ruby 的这种设计本质上代表了什么呢?实际上,这里的 times
虽然只是一个普通的类方法,但它的目的却与普通意义上的类方法不同,它的语义实际
上类似于 for 循环这样的语言基本语义,可以被视为一种自定义的基本语义。times 的语
义从一定程度上跳出了类方法的框框,向问题域迈进了一步!
  另一个例子来自 Eric Evans

的 用两个时间点构造一个时间段对象 ,普通设计:

  
  //Java 
TimePoint fiveOClock, sixOClock;

  TimeInterval meetingTime = new TimeInterval(fiveOClock, sixOClock);

  另一种 Evans 的设计是这样:
  //Java
TimeInterval meetingTime = fiveOClock.until(sixOClock);
  按传统 OO 设计,until 方法本不应出现在 TimePoint 类中,这里 TimePoint 类的
until 方法同样代表了一种自定义的基本语义,使得表达时间域的问题更加自然。
  虽然上面的两个简单例子和普通设计相比看不出太大的优势,但它却为我们理解流
畅接口打下了基础。重要的是应该体会到它们从一定程度上跳出了语言基本抽象机制的束
缚,我们不应该再用类职责划分、迪米特法则(Law of Demeter)等 OO 设计原则来看待
它们。
  2. 管道抽象
  在 Shell 中,我们可以通过管道将一系列的小命令组合在一起实现复杂的功能。管道
中流动的是单一类型的文本流,计算过程就是从输入流到输出流的变换过程,每个命令
是对文本流的一次变换作用,通过管道将作用叠加起来。在 Shell 中,很多时候我们只需
要一句话就能完成 log 统计这样的中小规模问题。和其他抽象机制相比,管道的优美在于
无嵌套。比如下面这段 C 程序,由于嵌套层次较深,不容易一下子理解清楚:

  //C
  min(max(min(max(a,b),c),d),e)
  而用管道来表达同样的功能则清晰得多:
  #!/bin/bash
  max a b | min c | max d | min e
  我们很容易理解这段程序表达的意思是:先求 a, b 的最大值;再把结果和 c 取最小值;
再把结果和 d 求最大值;再把结果和 e 求最小值。
  jQuery 的链式调用设计也具有管道的风格,方法链上流动的是同一类型的 jQuery