DevHeads.net

Template Method Pattern (GoF) without abstract methods in Ruby

Hi!

I have some questions re. the Template Method Pattern (GoF) in Ruby.

I need to update a simple management reporting system and have to add HTML to the so far text-based reports. Looks to me like the classical use case for the Template Method Pattern. I learned from Russ Olsen’s book »Design Patterns in Ruby« that we do not have / use / „make up“ abstract methods in Ruby. But a lot of infos concerning this topic on the web use these 'abstract‘ methods by raising some kind of error upon calling the methods (see [1, 2, 3]).

Does anybody out there in real ruby code bases actually use this approach with made up 'abstract‘ methods?

I’ve come up with three simple and obviously contrived example implementations without using abstract methods (and without meta programming):
a) Inheritance from a simple(r) use case [4]
b) Inheritance from an 'abstract‘ class [5]
c) Composition by mixin of an 'abstract' module with the concerning template method(s) [6]

Anything I might have missed here? And which way is most idiomatic Ruby (if at all;-)

Many thanks!

Cheers,
Michael

~~~
[1]: <a href="https://medium.com/@dljerome/design-patterns-in-ruby-template-method-753443f5a1c8" title="https://medium.com/@dljerome/design-patterns-in-ruby-template-method-753443f5a1c8">https://medium.com/@dljerome/design-patterns-in-ruby-template-method-753...</a>
[2]: <a href="https://github.com/pruett/ruby-patterns/blob/master/patterns/template_method.md" title="https://github.com/pruett/ruby-patterns/blob/master/patterns/template_method.md">https://github.com/pruett/ruby-patterns/blob/master/patterns/template_me...</a>
[3]: <a href="https://stackoverflow.com/questions/12873873/ruby-base-class-call-child-class-like-in-abstract-classes/12873925#12873925" title="https://stackoverflow.com/questions/12873873/ruby-base-class-call-child-class-like-in-abstract-classes/12873925#12873925">https://stackoverflow.com/questions/12873873/ruby-base-class-call-child-...</a>

[4]: Example 1 - Inheritance from a simple(r) use case:
# Represents a simple (text) report
class Report
def initialize
@title = 'My Report'
@text = ['Line 1', 'Line 2.']
end

def output_report
output = head
output << body
output << footer

output
end

def head
"#{@title}\n"
end

def body
output = ''
@text.each do |line|
output << "#{line}\n"
end

output
end

def footer
''
end
end

# Represents an HTML report, inherits from the simple text report
class HTMLReport < Report
def head
<<~HTML
<html>
<head>
<title>#{@title}</title>
</head>
HTML
end

def body
output = "<body>\n"
@text.each do |line|
output << "<p>#{line}</p>\n"
end
output << "</body>\n"

output
end

def footer
"</html>\n"
end
end

[5]: Example 2 - Inheritance from an 'abstract‘ class:
# Represents an abstract report / template method(s)
class Report
def initialize
@title = 'My Report'
@text = ['Line 1', 'Line 2.']
end

def output_report
output = head
output << body
output << footer

output
end
end

# Represents a text report
class TextReport < Report
def head
"#{@title}\n"
end

def body
output = ''
@text.each do |line|
output << "#{line}\n"
end

output
end

def footer
''
end
end

# Represents an HTML report
class HTMLReport < Report
def head
<<~HTML
<html>
<head>
<title>#{@title}</title>
</head>
HTML
end

def body
output = "<body>\n"
@text.each do |line|
output << "<p>#{line}</p>\n"
end
output << "</body>\n"

output
end

def footer
"</html>\n"
end
end

[6]: Example 3 - Mixin of an 'abstract' module with the template method(s):
# Template method(s) for a report
module Report
def initialize
@title = 'My Report'
@text = ['Line 1', 'Line 2.']
end

def output_report
output = head
output << body
output << footer

output
end
end

# Represents a text report
class TextReport
include Report

def head
"#{@title}\n"
end

def body
output = ''
@text.each do |line|
output << "#{line}\n"
end

output
end

def footer
''
end
end

# Represents an HTML report
class HTMLReport
include Report

def head
<<~HTML
<html>
<head>
<title>#{@title}</title>
</head>
HTML
end

def body
output = "<body>\n"
@text.each do |line|
output << "<p>#{line}</p>\n"
end
output << "</body>\n"

output
end

def footer
"</html>\n"
end
end

Comments

Re: Template Method Pattern (GoF) without abstract methods in Ru

By Austin Ziegler at 01/10/2019 - 14:23

GoF is not a recipe book.

-a

On Thu, Jan 10, 2019 at 11:59 AM Michael Schwarze <michael@schwarze-web.de>
wrote: