用 Ruby 踩踩四人帮

上上周在书店看到一本《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 动态选择调用方法多简单:

Ruby代码
arr.__send__ strategy


15.Singleton

module SingletonClass
  class << self
	# methods
  end
end

听说有人用两百行实现了严格的 singleton,膜拜中。

结论:
如果没有类型声明和继承,很多依赖性都会消失无踪,接口也无用武之地了。
设计模式大都是 Interface Hack 汇总,枯燥无味、思想僵化、限制创造力,早该下架了。
学设计模式不如学 Ruby Python。
Java 语言(注意不是平台)的弱点导致大量的冗余代码。
所谓从善如流,有功夫写冗余代码不如多写几个测试。
Java 语言(注意不是平台)的历史意义在于:将程序员从过早考虑效率的传统中解放了出来,现在也该功成身退了。

你可能感兴趣的:(用 Ruby 踩踩四人帮)