突然对Ruby实现的设计模式比较感兴趣,边学习边记录一下吧。 Template method模式是最简单的一种设计模式了,下文的解释引自追mm与设计模式一文( 没想到写这文章的哥们是个YD的青年啊,不过也有助于帮助理解设计模式 ):

Template method——看过《如何说服女生上床》这部经典文章吗?女生从认识到上床的不变的步骤分为巧遇、打破僵局、展开追求、接吻、前戏、动手、爱抚、进去八大步骤(Template method),但每个步骤针对不同的情况,都有不一样的做法,这就要看你随机应变啦(具体实现);

模板方法模式:模板方法模式准备一个抽象类,将部分逻辑以具体方法以及具体构造子的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。先制定一个顶级逻辑框架,而将逻辑的细节留给具体的子类去实现。


以一个例子来说, 写一个输出一个不同格式的report generater。可以输出html格式,也可以输出纯文本的内容。

这个例子是hard-coded输出html格式的report类,但是无法处理纯文本格式。

class Report
  def initialize
    @title = 'Monthly Report'
    @text =  [ 'Things are going', 'really, really well.' ]
   end
  def output_report
    puts( '')
    puts( '  ')
    puts( "    #{@title}")
    puts( '  ')
    puts( '  ')
    @text. each do |line|
       puts( "    

#{line}

"
)
     end
    puts( '  ')
    puts( '')
   end
end

如果你要处理纯文本,那么只能重新写一个类:
class Report
  def initialize
    @title = 'Monthly Report'
    @text =  [ 'Things are going', 'really, really well.']
   end
  def output_report(format)
     if format == :plain
      puts( "*** #{@title} ***")
    elsif format == :html
      puts( '')
      puts( '  ')
      puts( "    #{@title}")
      puts( '  ')
      puts( '  ')
     else
      raise "Unknown format: #{format}"
     end
    @text. each do |line|
       if format == :plain
        puts(line)
       else
        puts( "    

#{line}

"
)
       end
     end
     if format == :html
      puts( '  ')
      puts( '')
     end
   end
end


上面的代码是可以工作的,但是如果将来又有需要让你处理其他格式呢,再重写一遍啊?这个时候template method就起作用了。我们可以把公共的部分抽象出来,也就是说先制定一个顶级逻辑框架,而将逻辑的细节留给具体的子类去实现。Ruby本身并没有抽象类,那么我们可以像下面方式一样去模拟:
class Report
  def initialize
    @title = 'Monthly Report'
    @text =  [ 'Things are going', 'really, really well.']
   end
  def output_report
    output_start
    output_head
    output_body_start
    output_body
    output_body_end
    output_end
   end
  def output_body
    @text. each do |line|
      output_line(line)
     end
   end
  def output_start
    raise 'Called abstract method: output_start'
   end
  def output_head
    raise 'Called abstract method: output_head'
   end
  def output_body_start
    raise 'Called abstract method: output_body_start'
   end
  def output_line(line)
    raise 'Called abstract method: output_line'
   end
  def output_body_end
    raise 'Called abstract method: output_body_end'
   end
  def output_end
    raise 'Called abstract method: output_end'
   end
end


 那么我们可以实现处理html的子类了:
class HTMLReport < Report
    def output_start
        puts( '')
     end

    def output_head
       puts( '  ')
       puts( "    #{@title}")
       puts( '  ')
   end

   def output_body_start
      puts( '')
   end

   def output_line(line)
      puts( "  

#{line}

"
)
   end

   def output_body_end
      puts( '')
   end

   def output_end
       puts( '')
   end
end

同样,处理文本的子类:
class PlainTextReport < Report
    def output_start
     end
  
    def output_head
        puts( "**** #{@title} ****")
        puts
     end

     def output_body_start
     end

     def output_line(line)
        puts(line)
     end

     def output_body_end
     end

    def output_end
     end
end

 
看看结果:
 
report = HTMLReport.new
report.output_report
#=>


  Monthly Report


Things are going


really, really well.






report = PlainTextReport.new
report.output_report
#=>
** Monthly Report ****
Things are going
really, really well.