上上周在书店看到一本《Ruby设计模式》,捡起来 10 分钟看完,扔了(别问我为什么……)
下面用 Ruby 写写设计模式,顺便批一批 Java 和 Gof。
1.Factory 和 Abstract Factory
class Factory attr_accessor :product def produce @product.new end end class Product #.. end fac = Factory.new fac.product = Product fac.produce
Java写的工厂有这么简单,这么容易重用么?
2.Builder
# 工头 class Director def build_with builder acc = '' [:header, :body, :footer].each do |m| acc += builder.__send__ m if builder.respond_to? m end acc end end # 工人 class HTMLBuilder def header; '<html><title>html builder</title>';end def body; '<body>html builder</body>' ;end def footer; '</html>' ;end end class XMLBuilder def header; '<?xml version="1.0" charset="utf-8">';end def body; '<root>xml builder</root>' ;end end d = Director.new puts(d.build_with HTMLBuilder.new) puts(d.build_with XMLBuilder.new)
注意:Ruby的工序并不依赖于Builder的类,只要有方法签名就行了。
interface 这种束手束脚,加强耦合的东西完全不需要~
3.Prototype
依样画葫芦。
这里用一点 trick (evil ruby):
require 'evil' class Prototype # ... end class Concrete include Prototype.as_module end
4.Adapter
Ruby 包装方法易如反掌:
class Adaptee def talk; puts 'Adaptee';end end class Adapter < Adaptee alias talkee talk def talk puts 'before Adaptee' talkee puts 'after Adaptee' end end Adapter.new.talk
很多没学过设计模式的 Ruby 程序员天天用 adapter……
5.Composite
这个 pattern 是给没有多重继承又没法 mixin 的语言用的。
Ruby 只需要 include module。
6.Decorator
module Colorful attr_acessor :color end class Widget end w = Widget.new # w 作为 Widget 的实例,没有 color 方法 w.color = 'blue' rescue puts 'w has no color' w.extend Colorful # 现在 w 有 color 方法了 w.color = 'blue' puts w.color
可怜的 Java 程序员需要学习设计模式才能写出 decorator。
7.Flyweight
# 重量级对象的瘦身法:如果创建参数相同,则返回同一个对象 class FlyweightFactory class Glyph def initialize key @key = key sleep 1 # 睡一秒,以体现这个对象创建的“重量级” @square = key ** 2 @cubic = key ** 3 end attr_reader :key, :square, :cubic end def produce key @glyphs ||= {} @glyphs[key] || (@glyphs[key] = Glyph.new key) end end ff = FlyweightFactory.new g1 = ff.produce 2 g2 = ff.produce 2 puts (g1.object_id == g2.object_id)
不得不说 || 是很爽的语法。
另外 Ruby 的 Hash 可以用数组作 key,如果 Glyph 的构造函数需要更多参数,只需要把 produce 里的 key 改成 *key
8.Proxy
Proxy 和 Adapter 的区别只在接口上,它们在 Ruby 中是一样的。
这说明了:1.大道至简; 2.Gof 模式的语言局限性。
9.Chain of Responsibility
如果没有 proc,代码是做不到这么清晰简洁的:
class Chain def initialize @chain = [] end def add_handler &block @chain << block end def handle req @chain.each do |e| # 如果handler返回 false(未处理),则让下一个处理 result = e[req] return result if result end false end end c = Chain.new c.add_handler {|req| req == 1 ? "1:handled" : puts "1:not my responsibility" } c.add_handler {|req| req == 2 ? "2:handled" : puts "2:not my responsibility" } puts(c.handle 1) puts(c.handle 2)
10.Command
本质:一次调用可以同时执行多个方法。GUI 编程中处理事件很常用。
因为 Java 不能直接传递方法,所以把简单的问题复杂化了……
btw:真不理解 swing 优美在哪里……
class Command def initialize @executors = [] end # 另一种方法是让 executors 保存一组对象,每个都带 execute 方法 # ——但是这么简单的事情就需要一组接口,一组实现? def add_executor &block @executors << block end def execute @executors.each {|x| x.call } end end c = Command.new c.add_executor{ puts 'executor 1' } c.add_executor{ puts 'executor 2' } c.execute
Command 是和 Chain 很相似的东西,可能某天会有人写一本 "Pattern of Patterns" 吧。
11.Template Method
Java 一说模板,C++ 和 Ruby 就笑了。
例(偷懒“重用”一下写过的代码):
# 穷举法检验 de Morgan 定理 class Fixnum %w[a1 a2 a3 b1 b2 b3].each_with_index do |name, idx| define_method name, do self & (1<<idx) == 0 ? false : true end end end 0b1000000.times do |n| n.instance_eval %q[ if !((a1&&b1) || (a2&&b2) || (a3&&b3)) != !(a1&&b1) && !(a2&&b2) && !(a3&&b3) puts 'blah' end ] end
12.Iterator 和 Visitor
这些模式还是作古吧。
有太多简单的方式进行迭代(map, inject, each, each_with_index,sort ...)
关键点还是语言对泛型和匿名函数的支持。
13.Mediator
将各个类的相互依赖性扔到一个中介类之中。
老老实实的写个中介类?大概会像这样:
class Mediator def initialize seller, buyer @seller = seller @buyer = buyer end def sell @seller.sell end def buy @buyer.buy end end
发现问题了吗? Mediator 出现的根源还是静态类型(是不会推断的那种)带来的耦合。
Duck Typing (check respond_to? instead of class) 早已解耦,根本不需要中介。
14.Strategy
提供运行时选择策略(算法)的可能。
假设 Array 有两种方法:bubble_sort 和 quick_sort
按照 Gof 的教诲,我们可能会这样想:
class Array def sort options if options[:strategy].to_sym == :bubble_sort bubble_sort() elsif options[:strategy].to_sym == :quick_sort quick_sort() end end end arr.sort :strategy => strategy
根本就是没事找事……看看 Ruby 动态选择调用方法多简单:
arr.__send__ strategy
15.Singleton
module SingletonClass class << self # methods end end
听说有人用两百行实现了严格的 singleton,膜拜中。
结论:
如果没有类型声明和继承,很多依赖性都会消失无踪,接口也无用武之地了。
设计模式大都是 Interface Hack 汇总,枯燥无味、思想僵化、限制创造力,早该下架了。
学设计模式不如学 Ruby Python。
Java 语言(注意不是平台)的弱点导致大量的冗余代码。
所谓从善如流,有功夫写冗余代码不如多写几个测试。
Java 语言(注意不是平台)的历史意义在于:将程序员从过早考虑效率的传统中解放了出来,现在也该功成身退了。