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