Ruby设计形式系列之模板模式

Ruby设计模式系列之模板模式
写过java的同学一定对java中无处不在的设计模式印象深刻。相比较而已ruby在设计模式这一块就显得稍微有些拿不出手了。作为一门动态到极致的语言,设计模式在ruby中的应用可能不太传统,有些许的变种。但万变不离其宗,在这里我们就通过一些ruby代码所描述的例子来理解和学习一些基本的设计模式。

引用

注:本文中所有例子及代码均来自ruby design pattern一书,无任何本人原创,请勿对本文作者产生个人崇拜。

从这里开始:模板模式。

下面的代码实现了一个简单的模板,代码通俗易懂,应该很好理解。

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('<html>')
            puts(' <head>')
            puts(" <title>#{@title}</title>")
            puts(' </head>')
            puts(' <body>')
        else
            raise "Unknown format: #{format}"
    end
        @text.each do |line|
        if format == :plain
            puts(line)
        else
            puts(" <p>#{line}</p>" )
        end
        end
        if format == :html
            puts(' </body>')
            puts('</html>')
        end
    end
end

上面的代码判断了format的类型,如果是html则打印html模板,如果是plain text则打印普通的文字模板。

我们可以很容易的看出上述代码的一些缺陷:
  •     扩展性差。若再添加1个format的话代码改动量还是很大的;
  •     代码重复。print report的过程实际上都是一样的,每个format只是重复一下这个过程而已。

于是我们就需要一个更好的设计模式来解决这个问题。模板模式开始粉墨登场。

仔细的研究一下代码,我们发现下面的这些事情是一成不变的:

   
  •     打印header;
  •     打印title;
  •     打印报告的每一行;
  •     打印报告的结尾;

我们可以把上面的”模板”性质的工作定义在一个模板类中,每个format都继承自这个模板基类,实现如下:

#ruby1.9实现
#report.rb文件,模板基类

#encoding: utf-8
class Report
  def initialize
        @title = '我的报告'
        @text = %w[第一行 第二行 第三行]
  end #initialize

  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.encode('gb2312')
        end
  end

  def output_start
        raise '不能直接调用该抽象方法'.encode('gb2312')
  end

  def output_head
        raise '不能直接调用该抽象方法'.encode('gb2312')
  end

  def output_body_start
        raise '不能直接调用该抽象方法'.encode('gb2312')
  end

  def output_line line
        raise '不能直接调用该抽象方法'.encode('gb2312')
  end

  def output_body_end
        raise '不能直接调用该抽象方法'.encode('gb2312')
  end

  def output_end
        raise '不能直接调用该抽象方法'.encode('gb2312')
  end

end #class

# formater_report.rb文件
# 实现各种format

require './report'
class HTMLReport < Report
  def output_start
        puts '<html>'
  end

  def output_head
        puts '<head>'
        puts "    <title>#{@title.encode('gb2312')}</title>"
        puts '</head>'
  end

  def output_body_start
        puts '<body>'
  end

  def output_line line
        puts "    <p>#{line}</p>"
  end

  def output_body_end
        puts '</body>'
  end

  def output_end
        puts '</html>'
  end
end #class

class PlainTextReport < Report
    def output_start;end        

    def output_head
        puts "**** #{@title.encode 'gb2312'} ****"
        puts
    end

    def output_body_start; end

    def output_line line
        puts line
    end

    def output_body_end; end
    def output_end; end
end

HTMLReport.new.output_report
PlainTextReport.new.output_report


  • 在Report基类中,由于ruby没有抽象方法的概念,所以我们使用raise exception的方式来模拟抽象方法被调用时所出现的异常。
  • 另外因为ruby1.9中编码的问题,我们调用encode ‘gb2312′方法来将字符串转码成gb2312编码,这样在控制台中能正常的显示中文,不会出现乱码。