TigerKing 2009-12-18
在Ruby语言中,是一种比较简便的编程语言。我们可以通过它在编程中获得很大的快乐感。在这里我们会未大家介绍一下Ruby装饰模式的相关应用技巧。
Ruby装饰模式是设计模式之一,它允许你在运行时动态地为一个存在的对象添加特性.在一个对象有很多种可以有不同方式组合的可以影响其特性的
变量时特别有用(dingsea:没看懂?翻译这段比较YY,以上大概是出版社的风格,其实用例子比较容易理解,向下看)
这个短小精悍的Ruby装饰模式实现,总结了这个星球上我最爱的最火的动态语言.
我从Eric Freeman, Elisabeth Freeman, Kathy Sierra, 和Bert Bates他们写的杰出的Head First Design Patterns 一书中借取一个例子.
假设你要计算一杯咖啡的价钱.你有一个实现了cost()方法的咖啡类.此例中我们出于示例目的硬编码其价格:
class Coffee def cost 2 end end
很好.不过如果我们要知道一杯加奶的咖啡多少钱怎么办?我们有一个新类:
class WhiteCoffee def cost 2.4 end end
好,但是现在我们要加奶油的咖啡怎么办?再要撒些东西在上边呢?(原文"And sprinkles",个人理解,欢迎讨论).明显地,不停地创建新类会导致在应用中出现类爆炸.为不同的咖啡和其调料(糖,奶...)组合创建新类是不现实的,这样会变糟糕--如果我们有不同种类的咖啡怎么办?然后我们
还不得不加一些调料在这些不同的咖啡中.这不是一个好办法.下面进入Ruby装饰模式.像本文题目提到的那样,这就是Ruby的8行代码实现:
module Decorator def initialize(decorated) @decorateddecorated = decorated end def method_missing (method, *args) args.empty? ? @decorated.send (method) : @decorated.send (method, args) end end
这就是你所要的.你可以在任何你想要装饰的类中包含以上module.然后你可以使用这个装饰者就像直接使用装饰好的对象一样,默认地,所有传到装饰者的信息都会被转到被装饰的对象那儿.你可以根据需要装饰你的方法用来扩展:
class Milk include Decorator def cost @decorated.cost + 0.4 end end
那么怎样解决我们刚开头的咖啡问题呢?Ruby装饰模式在实战中的强大之处就在于他们可以像被装饰的对象一样工作(dingsea:嗯,大概是说,类A,被装饰过后,客户代码认为它还是A,呵呵).通过更进一步,你可以装饰其它的装饰者,只要它们有同样的接口.通过为不同的"扩展"创建装饰者,我们可以使用组合的装饰者创建咖啡类并计算此咖啡的总价.
class Whip include Decorator def cost @decorated.cost + 0.2 end end class Sprinkles include Decorator def cost @decorated.cost + 0.3 end end Whip.new(Coffee.new).cost #=> 2.2 Sprinkles.new(Whip.new (Milk.new(Coffee.new))).cost #=> 2.9
当然,为方便着想我们情不自禁创建几个工厂方法:
class CoffeeFactory def self.latte SteamedMilk.new(Espresso.new) end def self.cappuccino Sprinkles.new(Cream.new (Milk.new(Coffee.new))) end end order = Order.new order.add(Coffee.new) order.add(CoffeeFactory .cappuccino) puts order.total
由于Ruby的高动态语言特性,装饰模式并不是在运行时扩展类的唯一方法,当然,我只是喜欢ruby中用如此简单的方法实现一个模式.在ruby中有关装饰模式的更多信息和实现,包括generic decorators和可选择的传统装饰模式,请参考DecoratorPattern page at the RubyGarden.