The two patterns (template and strategy) seem similar. The template method has an abstract base class with a skeletal method that drives the processing that needs to vary. The processing is done by calling abstract methods that are supplied by the concrete classes. We select the variation that we want by picking the concrete classes.
For example, we have a Report
class, and an HTMLReport
class that inherits from Report. Our Report
class could look like this:
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
and our actual concrete class:
class HTMLReport < Report
def output_start
puts('<html>')
end
def output_head
puts(' <head>')
...
end
...
end
Actual use:
report = HTMLReport.new
report.output_report
The key thing is that the abstract class calls other methods for the variable parts of the algorithm, which we can then subclass (in this case through Ruby inheritance) and then work on the actual implementation.
However, some drawbacks (according to Russ Olsen): - uses inheritance - limits runtime flexibility... once we select a particular version of the algo, changing our mind is hard.
So, the strategy pattern: - take out the annoyingly varying chunk of code and isolate it in its own class. Then create a whole family of classes, one for each variation.
Example:
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
Now our Report
class looks like this:
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
So, correct me if I'm wrong, it looks like since the strategies all have the same interface, we can just delegate to them in the Report
class. The Report
class is called the context
by those GOF people.
but how does this allow us to switch strategies at runtime? We still call them like this right?
report = Report.new(HTMLFormatter.new)
report.output_report
What are the main differences?
Aucun commentaire:
Enregistrer un commentaire