When we have a complex algorithm including several steps to build something, which would be vary in the middle.
Now you need Template method.
It comes up with an example:
class Report def initialize @title = 'Monthly Report' @text = [ 'Things are going', 'really, really well.' ] end def output_report puts('<html>') puts(' <head>') puts(" <title>#{@title}</title>") puts(' </head>') puts(' <body>') @text.each do |line| puts(" <p>#{line}</p>" ) end puts(' </body>') puts('</html>') end end
The usage of the class is as below:
report = Report.new report.output_report
When they need something more? It will support plain text or RTF or PostScript.
The code will mess up with many if ... elsif ... else conditions
So, let's do the seperate thing:
Separate the Things That Stay the Same
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 end def output_start raise 'Called abstract method: output_start' end def output_head raise 'Called abstract method: output_head' end def output_body_start raise 'Called abstract method: output_body_start' end def output_line(line) raise 'Called abstract method: output_line' end def output_body_end raise 'Called abstract method: output_body_end' end def output_end raise 'Called abstract method: output_end' end end
class HTMLReport < Report def output_start puts('<html>') end def output_head puts(' <head>') puts(" <title>#{@title}</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 PlainTextReport < Report def output_start end def output_head puts("**** #{@title} ****") puts end def output_body_start end def output_line(line) puts(line) end def output_body_end end def output_end end end
Then the usage is really straight:
report = HTMLReport.new report.output_report report = PlainTextReport.new report.output_report
Ruby doesn't support abstract methods and abstract classes, then we can use the methods which raise exception meet our demand.
"In the Template Method pattern, the abstract base class controls the higher-level processing through the template method; the subclasses simply fill in the details."
Then it goes to hook methods. but really, I don't agree with the writer's opinion about the example
Report with the default method implementations. Maybe the default implementations will brought in some wrong format if the coder obmit to implement some methods, such as missing the output_start or output_end or something context related.
So maybe we should implement the must be right thing for the common abstract class.
Lazziness must be secondary in front of correctness
Then we go to duck typing issues:
If it looks like a duck and quacks like a duck, then it is a duck~
The statically typed languages are working like aristocracies, they always ask about your genealogy.
The dynamically typed language are working like meritocracies, they only concern about what you have rather than where do you get the methods from.
The writer wrote it at the end of the part~
Dynamically typed languages rarely ask about an object’s ancestry; instead, they simply say, “I don’t care who you are related to, Mac. All I want to know is what you can do.”