写过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编码,这样在控制台中能正常的显示中文,不会出现乱码。