【发布时间】:2017-06-07 22:59:09
【问题描述】:
这两种模式(模板和策略)看起来很相似。模板方法有一个抽象基类和一个驱动需要变化的处理的骨架方法。通过调用具体类提供的抽象方法来完成处理。我们通过选择具体的类来选择我们想要的变体。
例如,我们有一个 Report 类和一个继承自 Report 的 HTMLReport 类。我们的Report 类可能如下所示:
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
def output_start
raise StandardError
end
def output_head
raise 'This needs to be implemented by the concrete class'
end
...
end
以及我们实际的具体类:
class HTMLReport < Report
def output_start
puts('<html>')
end
def output_head
puts(' <head>')
...
end
...
end
实际使用:
report = HTMLReport.new
report.output_report
关键是抽象类为算法的可变部分调用其他方法,然后我们可以子类化(在这种情况下通过 Ruby 继承),然后处理实际实现。
但是,有一些缺点(根据 Russ Olsen 的说法): - 使用继承 - 限制了运行时的灵活性...一旦我们选择了特定版本的算法,就很难改变主意。
所以,策略模式: - 取出烦人的代码块并将其隔离在自己的类中。然后创建一个完整的类族,每个变体一个。
例子:
class Formatter
def output_report(title, text)
raise 'Abstract Method called'
end
end
class HTMLFormatter < Formatter
def output_report(title, text)
puts('<html>')
puts(' <head>')
puts(" <title>#{title}</title>")
puts(' </head>')
...
end
现在我们的Report 类看起来像这样:
class Report
attr_reader :title, :text
attr_accessor :formatter
def initialize(formatter)
@title = 'Monthly Report'
@text = ['Things are going', 'really, really well.']
@formatter = formatter
end
def output_report
@formatter.output_report(@title, @text)
end
end
所以,如果我错了,请纠正我,因为这些策略都有相同的接口,我们可以在 Report 类中委托给它们。 Report 类被那些 GOF 人称为 context。
但这如何让我们在运行时切换策略?我们还是这样称呼他们吧?
report = Report.new(HTMLFormatter.new)
report.output_report
主要区别是什么?
【问题讨论】:
-
如果你想改变模板方法版本中的格式,你必须创建一个全新的报告(因为你需要一个不同的子类)。在策略版本中,您只需为现有报告分配一个新的格式化程序。
标签: design-patterns