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 Greg Navis at 01/11/2019 - 06:09

I personally do add "abstract" methods that raise errors. It makes things
more explicit than having being thrown NoMethodError in case I forget to
implement it and makes it simple to identify which methods I need to
implement/override in subclasses.

I haven't seen your code but assuming the data structures representing the
report are traversed the same for all formats you have two options:

1. Use the template method pattern as you said.
2. Make a ReportGenerator class that accepts ReportRenderer as an argument.

It's difficult to say more without seeing the code though.

Greg

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

By Michael Schwarze at 01/11/2019 - 17:34

Hi Greg,

Great, so it’s not just examples in blog posts, but real.

Making your design decisions explicit by using these 'abstract‘ methods sounds like a good idea to me.

Will think about this and try it...

Many thanks for your advice!

Cheers,
Michael

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

By Dan Itsara at 01/11/2019 - 07:23

Not sure whether or not this is applicable in your situation, but could you
instead write a method that accepts a block?

Example:

def foo(i, really, want, these, args = {})
yield(i, really, want, these, args) if block_given?
end

(obviously you could do other things in your method as well to DRY up
duplicate logic)

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

By Michael Schwarze at 01/11/2019 - 17:37

Hi Dan,

That’s an additional way I haven’t thought about so far and I’ll try!

Thanks & Regards,
Michael

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>

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

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

GoF is not a recipe book.

-a

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

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

By Michael Schwarze at 01/11/2019 - 05:07

Hi Austin,

Thanks; I’m from a different programming language background and curious whether there is kind of a 'pattern of implementation‘ for implementing the Template Method Pattern in Ruby. My 'research‘ so far brought contra-dictionary results: the book on design patterns in Ruby saying not to use 'abstract‘ methods, but showing them in the respective examples and the examples found on the web |1, 2, 3] showing these 'abstract‘ methods in Ruby, too.

Hence my first question, to understand which approach to implementing the Template Method Pattern Ruby developers have chosen. I think that’s one of the ideas of design patterns: to have and guide a discussion around common or recurring design challenges. How do you approach this in Ruby? Do you prefer to inherit from a simpler use case / class, an 'abstract‘ class with these made up 'abstract‘ methods (no content, just raising an error) or do you prefer mixin modules? Why so?

As I’ve got a real world problem in one of my Ruby projects to solve, which reminded me of the Template Method Pattern, I’ve made up three possible design examples [4, 5, 6] for solving this, which would all work. But which way is ’the Ruby way’ here? So it’s actually more about trying to better understand OO in Ruby than about pattern.

Does this make sense to anybody?

Many thanks!

Cheers,
Michael

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>

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

By Austin Ziegler at 01/11/2019 - 12:52

On Fri, Jan 11, 2019 at 4:08 AM Michael Schwarze <michael@schwarze-web.de>
wrote:

But that’s my point. If you’re trying to implement the `Template Method
Pattern`, you’re trying to use GoF as a recipe book. This is a mistake and
will only hinder your learning of Ruby. What problem are you actually
trying to solve? I have yet to see a place where I have needed to implement
most of the GoF patterns in Ruby—some of these are because the language
provides features that supplant the need for such a pattern. In Ruby, you
quite *literally* don’t need abstract methods.

class Processor
def process
step1
step2
step3
finalize
end

private

def finalize
# finalize the process here
end
end

class Foo < Processor
private

def step1; end
def step2; end
def step3; end
end

If I do Processor.new.process, I will get a NoMethodError because I have no
implementation of `step1`, `step2`, or `step3`. If I do `Foo.new.process`,
it works. You don’t need abstract methods in Ruby because the methods don’t
have to exist during compilation, because there’s no compilation phase or
static method resolution.

I don’t try to solve the template method pattern because I’ve been
developing long enough that I recognize that pattern descriptions are
forensic descriptions of common implementation patterns—often not necessary
in languages with advanced and/or functional features (they have
*different* patterns that can be forensically described) and not an
instruction manual or language comparison tool.

-a

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

By Michael Schwarze at 01/11/2019 - 18:19

Hi Austin,

Many thanks for your patience and your explanations!

Sorry, my English is probably worse than my Ruby ;-) I’m not really focussed on implementing the pattern 'by the book‘ but I think my problem is in the realm of the Template Method Pattern...

A simple management report which was text-only so far and should get an additional HTML output now; both following the same process / outline in generating the slightly differing output. That’s why I supplied the three made-up code examples with my initial email.

That was my understanding from the book, too. I did a web research but the results confused me, as they contained mainly abstract methods in Ruby. Hence my request to this list.

Code helps - that’s the discussion I was looking for! That’s close to my example 2...

Ok, I get this. So you are suggesting inheritance here, but why use a class you will probably never instantiate? Why not mixin a module with the common methods (my example 3)?

module Processor
def process
step1
step2
step3
finalize
end

def finalize; end
end

class Foo
include Processor

def step1; end
def step2; end
def step3; end
end

class Bar
include Processor

def step1; end
def step2; end
def step3; end
end

Or, when preferring inheritance, why not inheriting from the simple use case (my example 1):

class Foo # simple text
def process
step1
step2
step3
finalize
end

def step1; end
def step2; end
def step3; end
def finalize; end
end

class Bar # complex HTML
def process
step1
step2
step3
finalize
end

def step1; end
def step2; end
def step3; end
def finalize; end
end

All three approaches do work but which one would do Ruby experts prefer for solving this problem?

Cheers,
Michael

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>

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

By Austin Ziegler at 01/11/2019 - 18:53

If you’re making a report generator, you would want to use either
inheritance or decoration.

class ReportGenerator
def initialize(thing_to_report_on)
@thing_to_report_on = thing_to_report_on
end

def generate
parse
format
output
end
end

You might implement `parse` and `output` the same in the `ReportGenerator`,
but `format` would be different per sub-class.

The ultimate decoration, however, would be to have your generator, and then
do:

generator = ReportGenerator.new(data_for_report)
generator.extend(HTMLReportFormatter)
generator.generate

That’s a per-object mixin.

You can also do this with separate pieces:

class ReportGenerator
def prepare
end
end

class HTMLReportFormatter; def format(prepared_report); ...; end; end
class TextReportFormatter; def format(prepared_report); ...; end; end

rg = ReportGenerator.new(item).prepare
HTMLReportFormatter.new(rg)

What makes the most sense is what reads best to you and what will be
easiest to maintain.

For me, that depends on the rest of the code and what I’m trying to solve.
I have pretty much solved all of these in different ways every time, but
with Ruby I tend to shift toward duck-typing as much as possible.

-a

On Fri, Jan 11, 2019 at 5:20 PM Michael Schwarze <michael@schwarze-web.de>
wrote:

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

By Michael Schwarze at 01/13/2019 - 05:22

Hi Austin,

Many thanks again! Took me a while to get my head around this per-object mixin but I think I got it now. I have only used include / extend to 'import‘ source code into my classes so far and wasn’t aware of this mixin feature at runtime / per object. But then Ruby being a dynamic language this make of course perfect sense.

Will stick with this approach for my reporting problem…

Cheers,
Michael

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>

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

By Andy Jones at 01/11/2019 - 05:28

My 10p:

Many of these patterns were designed for older languages with more basic features. I looked up Template Method -- and, please correct me if I've got the wrong end of the stick -- this is just polymorphism, a basic feature of OOP?

I know Ruby coders often shy away from inheritance (with good reason) but personally this is probably one of those times to consider using it.

Obviously Ruby doesn't have abstract classes or abstract methods. But AFAIK the idiomatic way to simulate an abstract method in Ruby is:

#########
def foo
fail NotImplementedError, "You need to override this method"
end
#########

...and of course an "abstract class" is any class that has an abstract method in it.

I'm sure that there are dozens of other ways to achieve this goal, other than using inheritance. And, as I say, perhaps I've misunderstood the problem. But for my money if it looks like polymorphism then you want inheritance.

Click here to view Company Information and Confidentiality Notice.<http://www.jameshall.co.uk/index.php/small-print/email-disclaimer>

Please note that we have updated our privacy policy in line with new data protection regulations. Please refer to our website to view the ways in which we handle your data.

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>

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

By Michael Schwarze at 01/11/2019 - 17:27

Hi Andy,

That’s exactly what I meant with 'abstract‘ method and what got me confused: As you said and Russ Olson wrote, Ruby doesn’t have them. Should we then really simulate them? The examples I found on the web did it but do they exist in real Ruby code bases / do you use them or are they just examples?

Many thanks!

Cheers,
Michael

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>