Re: Template Method vs Strategy



On 2006-05-31 11:21:34 -0500, "H. S. Lahman" <h.lahman@xxxxxxxxxxx> said:

Bob, you have to get out of the muck of OOPL mechanisms and look at the OOA/D of what the Template pattern is about...

Let's recall what we were discussing:

class GenericTableAlgorithm {
public:
bool Process() {
bool retVal = true;
while (Record* r = /*get record*/) {
if (Filter(*r))
retval &= Process(r->GetKey());
}
return retVal;
}

private:
virtual bool Filter( const Record& ) {
return true; // default all records.
}

virtual bool ProcessRow( const PrimaryKey& ) =0;
};

The Process function encapsulates the main loop of an arbitrary number of different search and process applications. Each of those different applications would be represented by a different derivative that implemented Filter and ProcessRow differently.

Again, the only hardwiring is in name only. Filter and ProcessRow are
abstract methods -- mere shadows. They will be implemented in
derivatives.

Forget about abstract methods. That is OOPL window dressing.

Every time you stress the difference between OOPL and OOD you stress the difference between OO and Translation. No, the abstract methods are not window dressing to the Template Method pattern; they are the essence of the pattern!

The issue here is that the abstract class has defined a functional decomposition via the public/private chain. There is still only one object in this application that is decomposing the entire application solution.

No application was shown. This class is a reusable part of an indefinite number of different applications. It's true that this small part of those applications will consist of a single object; but it will also consist of two classes that are well partitioned.


And process() itself makes a mockery of cohesion and logical
indivisibility of responsibilities.

No, it does't. It's a very nice little function that abstracts the notion of select and process.

Not only is it responsible for
coordinating filter() and processRow(), it is also responsible for all
the details around forming SQL queries (or whatever), decoding datasets,
and processing row data -- all at completely different levels of
abstraction.


Balderdash! The process() method is entirely abstract. It implements
a simple algorithm and depends on none of the details.

Again, this is OOPL implementation muck. process() has to be implemented in the derived class and that is the problem -- it has too many responsibilities to implement. It can't be cohesive and logically indivisible.

No, Process is already implemented in the base class. The derived classes will implement Filter and ProcessRow. And as for implementing SQL, the article didn't show it, but that would likely go in another abstract method of the class to be implemented by the derivative. Though it could also be done in a Gateway class. The article wasn't trying to make a point about that so it just left it out.

Want to bet there are going to be several more private
methods in the decomposition before the class implementation is done?


There won't be any in the base class HS. There MAY be some in the
derived classes; but that's where they belong. The base class will
remain very clean and indendent of all the hoary little details. There
will never be anything to do with SQL in the base class. Indeed, the
base class could continue to be used in an environment where there is
no RDBMS at all. The only thing it depends upon is the concept of a
row (which would be better renamed as record).

There is still only one object and it is implementing all of the application's responsibilities with three methods. It doesn't matter whether there is subclassing or not. The object is still a god object.

Nope, because the code is distributed; and the dependencies are well managed.

On a separate note, I also don't see the GoF Template or any other
design pattern in the example.

Then you need to read both the GOF book and the example again; because
the entire article is a treatise on Template Method and Strategy; and
the author made that about as clear as he could have. It would have
been just as clear if he hadn't used the names.

Au contraire. The Template pattern does not exist in a vacuum.

Neither does this example. It presumes that someone will call Process, and deal with the result. That someone is not shown.

It exists to resolve complex dynamics in a relationship with some client. To do that it employs things like behavior substitution through polymorphic dispatch. But none of that is present here. There is no client (unless you count main()) and there is no substitution because there is only one derived class. Hence there is no polymorphic dispatch (regardless of what the OOPL implements when it encounters an abstract base class declaration).

I could write an application that implemented many different derivatives of GenericTableAlgorithm. I could then call Process on any or all of them, any time I wanted. In each case the high level Process algorithm would be provided by the base class, and the details of Filter and ProcessRow would be provided by one of the derivative. So the polymorphic dispatch is, in fact, quite useful.

But the polymorphic dispatch is also quite useful in the case of a simple application that implements only one derivative of GenericTableAlgorithm; because GenericTableAlgorithm can come from a reusable library.

The use of Template here is a sham. The author is using the Template /form/ for an entirely different purpose.

The author was comparing and contrasting Template Method and Strategy. The example is pedagogical. It's also fairly common in real applications.

The author has a god object and needs to organize the complex functionality.

No, he had a point to make about the two different patterns, and so he concocted a realistic example.

He does that through functional decomposition using private methods as in Template. Hey, look! I'm using inheritance and abstract classes. Hey, look! I'm using a GoF pattern. This MUST be good OO development.

You are talking about Herb Sutter. Herb is an extremely experienced software developer who doesn't go around making absurdly naive statements like that. Even if the author weren't Herb, it wouldn't matter because the article describes the trade-off between Template Method and Strategy quite nicely. And it uses a perfectly realistic, of somewhat attenuated example to do it.

Note that for this application I could implement exactly the same method implementations with no abstract class and no inheritance.

But you would be missing the point of the article, the point of the pattern, and the point of OOD.

All I need to do is define the public/private decomposition of the derived class in a single standalone class. Would you still argue it was not a god class?

Then it would be a god class. But the use of polymorphic dispatch here is THE ESSENCE of the pattern, and provides a powerful decoupling between the high level abstract loop and the low level details. HS, this is absolutely fundamental to the concept of Template Method, and OOD in general.


Every GoF pattern resolves a *:* or 1:*
relationship between some client and a service where collaboration is
too dynamically complex to describe with a simple association.


Nonsense. That's a generalization that doesn't hold for quite a few
GOF patterns like Template method, Factory Method, and Command.

Again, you've got to get out of the muck of OOPL implementations. Stop looking at the code in the example implementations and look at the Big Picture of what the pattern is doing at the OOA/D level.

It's important to remember that the "muck" as you call it, is what gets the work done; and where most of real design takes place. Drawing a few high level pictures is the easy part. Getting the dependencies managed at the code level is where things get tough.

For example, consider Factory Method:

[Client]
| *
|
| R1
|
| creates
| *
[Product]

That is not factory method. Factory method is:

class Base {
public: void DoSomething() {
SomeObject* o = makeObject();
....
}

private: virtual SomeObject* makeObject() = 0;
}

class Derived : public Base {
private:
virtual SomeObject* makeObject() {
return /* some object derived from SomeObject */
}
}

The use of inheritance is CRITICAL to the Factory Method pattern. It's what the pattern is all about. This pattern is not to be confused with the Abstract Factory pattern which uses a different mechanism to solve the same problem.

The problem the GoF pattern is addressing is that there are several different flavors of [Product], each requiring different construction techniques and decisions about selecting the right ones. That dynamic complexity is just too much to deal with via a simple *:* association. In addition, it would trash [Client]'s cohesion if it had to understand all the rules and policies of instantiation [Product]s. So the GoF Factory Method pattern reifies the relationship:

[Product] [Factory]
A A
| |
+-------.... +--------+----....
| * R1 1 |
[ConcreteProduct] ----------- [ConcreteFactory]
creates

OK, you are describing the Abstract Factory pattern, not the Factory Method pattern.


The Client hasn't gone away; it just talks to [Factory] in a well decoupled fashion. The GoF pattern has just reified the *:* relationship to two 1:* relationships. [The GoF doesn't think it is important to show the clients explicitly in their structure diagrams.

And Sutter didnt think it was important to show it in his article. But the exact same structure that you draw up there applies to the article.

Probably because the relationship between [Client] and the pattern in hand will always be the same. But the clients are always there.]

Compare that to Strategy:

[Client]
| *
|
| R1
|
| invokes
| *
[Context]

Again, invoking different flavors of [Context] based on some sort of dynamic context. Again, too much to describe via a simple association between [Client] and [Context]. So we reify again:

* R1 invokes 1
[Context] ----------------------------- [Strategy]
A A
| |
+------.... +----------+-----....
| |
[ContextA] [StrategyA]

The [Client] is still here; it's just talking to [Strategy] through [Context] now.

Agreed. No problem. And implied in the Sutter article.

There is no substantive difference here. Both patterns reify a *:* relationship to two 1:* relationships, use polymorphic dispatch to decouple [Client] from the context details, employ delegation, and describe explicit behavior substitution for the [Client]/[Context] collaboration.

Precisely. In fact, Abstract Factory IS strategy applied to the problem of creation. Just as Factory Method IS Template Method applied to the problem of creation.

The only thing that makes these three patterns unique compared to the others is that the delegation for these is from [Context] while the delegation for patterns like Template is from [Client]. That is just as cosmetic as reifying R1 to be between superclasses vs. between subclasses.

All of this is facinating, but getting back to your original assertion that the GOTW article proposed a terrible OO design. You are wrong. It posed a well known coorespondence between the Template Method and Strategy patterns. The god object argument is bogus because A) there is more than one class. B) The classes are nicely decoupled. and C) there really is an implied client, so the classes don't really stand alone.

My point, though, is that the paper example /doesn't/ use polymorphic dispatch.

It certainly does.

There is only one derived class defined so there is no substitution, which is a crucial element of Template.

The implication is that there will be many different derived classes.

Show me a second object. Show me a second derived class.

HS, the article doesn't even show you ONE derived class. That's a dead givaway (if the rest of the article wasn't, which it was) that there are far more than one derivatives possible.

--
Robert C. Martin (Uncle Bob)  | email: unclebob@xxxxxxxxxxxxxxxx
Object Mentor Inc.            | blog:  www.butunclebob.com
The Agile Transition Experts  | web:   www.objectmentor.com
800-338-6716                  |



.



Relevant Pages

  • Re: Template Method vs Strategy
    ... without bothering to learn OOA/D. ... you have to get out of the muck of OOPL mechanisms and look at the OOA/D of what the Template pattern is about... ... There is no client ) and there is no substitution because there is only one derived class. ...
    (comp.object)
  • Re: Blue ghost and sticky lines, etc.
    ... and save the presentation as a PowerPoint template ... you will get two control handles coming out ... Yes, the "fill with pattern" feature, eh... ... and click on the Picture tab. ...
    (microsoft.public.mac.office.powerpoint)
  • Re: Cutting patterns on the tails of raffters or perlins
    ... I would be intrigued to see if using a good, corded jig ... you could maybe follow a pattern and not route at all. ... them yanked from the template holder and even broken those suckers. ... I am thinking of using my router ...
    (rec.woodworking)
  • Re: Wanting to Know!!!
    ... We spent years developing and using a template for thinking and behaving in ... Now we are outside that pattern, ... reach for the smokes, since I quit, before I remembered I don't smoke. ...
    (alt.support.stop-smoking)
  • w3c Schema naming patterns and template-based schema generation
    ... consistent naming pattern such that, for instance, a Resident could appear in ... a ResidentSet, and could be referred to by a ResidentRef, and the same pattern ... in the schema, though, and results in a need to enforce consistency in how the ... A solution I've come up with is to have a template schema file in which any ...
    (comp.text.xml)