极地银狐.NET

  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
  144 随笔 :: 11 文章 :: 1249 评论 :: 9 引用
[原文: Decorator Pattern with Ruby in 8 lines]
[中文名:Ruby中8行代码实现Decorator模式]
[出处: http://www.lukeredpath.co.uk/2006/9/6/decorator-pattern-with-ruby-in-8-lines]
[作者: Luke Redpath ]
[译者: 极地银狐.NET]

装饰模式是设计模式之一,它允许你在运行时动态地为一个存在的对象添加特性.在一个对象有很多种可以有不同方式组合的可以影响其特性的

变量时特别有用(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的8行代码实现:


module Decorator
  def initialize(decorated)
    @decorated = decorated
  end

  def method_missing(method, *args)
    args.empty? ? @decorated.send(method) : @decorated.send(method, args)
  end
end

这就是你所要的.你可以在任何你想要装饰的类中包含以上module.然后你可以使用这个装饰者就像直接使用装饰好的对象一样(原文:You can then use that decorator as if it was the object it is decorating,我就直说了,呵呵)默认地,所有传到装饰者的信息都会被转到被装饰的对象那儿.你可以根据需要装饰你的方法用来扩展:

class Milk
  include Decorator

  def cost
    @decorated.cost + 0.4
  end
end
那么怎样解决我们刚开头的咖啡问题呢?装饰模式在实战中的强大之处就在于他们可以像被装饰的对象一样工作(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.

最后,在使用装饰模式时保持某些恒等性是很好的.上边提到的RubyGarden的网页里是其中一种方法.同时我们没有使用继承,保留继承这一特性是很好的:

CoffeeFactory.cappucino.kind_of? Coffee
#=> true

我能想到很多种方法扩展我的装饰模式来完成功能,但是我把这些留给读者.看你们的啦.

更新:在经典Ruby-fashion中,我8行代码的装饰模式被,嗯,Trevor Squires 0行的代码所打败.通过使用modules, super和extend,Trevor带来了这个同样保持恒等性的alternative solution,I服了U(原文:Bow down to his Ruby-fu).

更新2:没人会停止不前.我看了Trevor的Ruby-fu(dingsea:没懂这个-fu是啥)并加入了一点我自己的东西.我仍然觉得我的装饰模式实现会有分量些,加入一些语法溏块(dingsea:应该是指易读性)

class Milk
  include Decorator
end

class Whip
  include Decorator
end

class Sprinkles
  include Decorator
end

# normal coffee
Coffee.new

# coffee with milk, whip and sprinkles
Coffee.with :milk, :whip, :sprinkles

self.with方法可以把自己打包入一个可装饰module中,这处是它的具体实现.Trevor的方法仍然在保持对象恒等性上有优势,但我有方法解决它.不过,唉,半夜一点了,该睡觉了(dingsea:我翻译的时候也是1:28AM啊,你睡的是不是太早了?)

更新3:Trevor用他自己的self.with实现进行了反击.个人角度讲我比较喜欢Coffee.with :sym, :sym式的语法胜过于offee.with Module, Module式的语法,当然这只是个人喜好.通过他的extend实现,可以消除符号对类的欺骗性(dingsea:越看越晕,指教),而且仍然保持恒等性,他的FU仍然很强.

然而,下边是Trevor的方法:你可以使用一次extend来装饰对象,但是要是我要撒两次东西怎么办?

Sprinkles.new(Sprinkles.new(Coffee.new))

# or with a bit of sugar (no pun intended)
Coffee.with :sprinkles, :sprinkles


posted on 2006-12-12 01:52 极地银狐.NET 阅读(2281) 评论(6)  编辑 收藏 所属分类: 开发人生代码天下

评论

#1楼  2006-12-12 02:01 木野狐      
不错
  回复  引用  查看    

#2楼  2006-12-12 15:30 580k [未注册用户]
使用580k.com帮您关注此blog更新

580k是一种WEB形式的网页监控工具(网址:http://***/).所谓网页监控工具,用其首页的描述,就是:您关注的网页内容发生变化时,580k会将变化的内容用邮件通知您.
580K作为WEB工具,其提供的功能是有实际应用的,相信一些需要每天关注大量信息的人,如公司老总、炒股者、网络编辑、情报员、论坛灌水爱好者、新闻评论员等,会非常喜欢使用它的.
  回复  引用    

#3楼  2006-12-12 22:26 FantasySoft      
还是觉得Python的decorator实现比较爽:IronPython0.9.3发布了 —— 介绍一下Decorator

  回复  引用  查看    

<a target=_blank href=http://www.66soft.com/><img src=http://www.66soft.com/Linkpic/2007261625564033.gif alt=66软站 border=0></a>
你好,你能给我的站点做一个友情链接吗.
我们的友情链接申请添加是:http://www.66soft.com/service/Reglink.aspx
我们的代码是:
文字:
<a target="_blank" href="http://www.66soft.com/" title="66软站,www.66soft.com">66软站(件)</a>
谢谢你呀.可以吗.要是能合作我会很开心的
QQ:745747330
网站简介:
<h1>66软站,66软件,66软站,66soft,程序开发,项目管理,市场策划,电脑百科,家庭生活,免费生活,两性生活,免费资料,免费论文,游戏开发,在线学习,网站运营,程序员,考试政策,考题,考试答案,考试资料,考试过关</h1>

<h1>66软站,66软件,致力于打造国内一流的网络平台,给学子,求知者提供动力,做国内优秀互联勾通平台,66软站,66软件</h1>

<a href="http://www.66soft.com"">http://www.66soft.com" title="66软站,66软件,66软站,66soft,程序开发,项目管理,市场策划,电脑百科,家庭生活,免费生活,两性生活,免费资料,免费论文,游戏开发,在线学习,网站运营,程序员,考试政策,考题,考试答案,考试资料,考试过关">66软件,66软站</a>

<a href="http://www.66soft.com"">http://www.66soft.com" title="66软站,66软件,致力于打造国内一流的网络平台,给学子,求知者提供动力,做国内优秀互联勾通平台,66软站,66软件">66软件,66软站</a>
  回复  引用    

Ruby-fu的意思是Ruby功夫,呵呵。英文把功夫翻译成kong-fu,ruby世界里有些人喜欢起这样的名字xxx-fu,可能是因为觉得ruby是从东方来的。
  回复  引用    

#6楼  2008-04-23 18:56 笨→鸟(Bird)      
看不懂。。
  回复  引用  查看    


标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2006-12-13 00:21 编辑过
"五向定位"职业成长路线公开课(上海、南京、大连)
Google站内搜索


相关链接: