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>