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

Michael Schwarze <[email protected]>
Newsgroups gmane.comp.lang.ruby.general
Message-ID <[email protected]>
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

> Am 11.01.2019 um 23:53 schrieb Austin Ziegler <[email protected]>:
> 
> 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 <[email protected]> wrote:
> Hi Austin,
> 
>> Am 11.01.2019 um 17:52 schrieb Austin Ziegler <[email protected]>:
> 
> Many thanks for your patience and your explanations!
> 
>> 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.
> 
> 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...
> 
>> What problem are you actually trying to solve?
> 
> 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.
> 
>> In Ruby, you quite *literally* don’t need abstract methods.
> 
> 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.
> 
>> 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
> 
> Code helps - that’s the discussion I was looking for! That’s close to my example 2...
> 
>> 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.
> 
> 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:[email protected]?subject=unsubscribe>
> <http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>
> 
> 
> --
> Austin Ziegler • [email protected][email protected]
> http://www.halostatue.ca/ • http://twitter.com/halostatue
> 
> Unsubscribe: <mailto:[email protected]?subject=unsubscribe>
> <http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>


Unsubscribe: <mailto:[email protected]?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.