mercredi 7 juin 2017

What is the difference between template pattern and strategy pattern?

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