Re: Template Method vs Strategy
- From: "H. S. Lahman" <h.lahman@xxxxxxxxxxx>
- Date: Wed, 31 May 2006 16:21:34 GMT
Responding to Martin...
You probably don't want to hear this but I think the example is terrible
OOA/D. B-) It's a good example of what happens when procedural
developers convert to OO development by going directly to OOPL coding
without bothering to learn OOA/D.
Sigh. HS, what you call OOA/D is not what most people call OOA/D.
What you call OOA/D has more to do with the particular religion of
translation (or should we say MDA).
I am really disappointed that you keep making this nonsense assertion. There is nothing that I advocate on these forums that can't be found in some form in any classic OOA/D book. You should know that because you wrote an OOD book two decades ago. A translation OOA model looks exactly the same as an elaboration OOA model and it is constructed with exactly the same OO principles.
Translation only enters the picture by providing automation after the OOA model is done. MDA is essentially only relevant to the tools that support OOA/D/P and MDA supports both translation and elaboration tools. The only thing application developers need to know about MDA is the profile defined by the A&D methodology they are using.
Basically you have a god class that is doing everything.
I don't see that. We have a high level algorithm that is deferring
it's details to lower level classes. That's not the same thing as a
GOD class doing everything.
The example application has exactly one object that does /everything/. How is that not a god class? Are you really suggesting that it would never occur to you that problem space abstractions like Record, Table, and Filter might have something to to with solving the application problem? Are you seriously suggesting that it is reasonable for process() to encapsulate all of the SQL and dataset conversion boilerplate in addition to and at the same level of abstraction as coordinating the record selection and processing activities? Get real; that position is just indefensible.
It's classic
procedural development where the private methods represent lower level
nodes in a functional decomposition tree.
No, it's not. Classic procedural development would have statically
bound calls to the lower level nodes. In both the Template Method and
Strategy case those calls are dynamically bound, and therefore there is
no strong dependence of the high level algorithm on the lower level
details. The dependency has been reduced to a dependency on the names
and signatures of the implementing methods, but not any particular
implementation.
It certainly is and it has nothing to do with binding. process() is a high level method that coordinates two lower level methods (i.e., the sequence of execution is hard-wired in process' implementation). In addition you cannot specify what process() does without also specifying what the private methods do. That makes process() a high level node in a functional decomposition tree with the other methods as lower-level children.
This IS what OO is all about. Nygaard and Dahl invented OO by moving
the function frame from the stack to the heap and creating vector
tables within that frame to indirectly call functions. This breaks
dependence on particular implementations but not on names and
signatures. Translation/MDA use an entirely different strategy and
mechanism to achieve decoupling that breaks even the dependence on
names and signatures; it is a mistake to call it OOA/D.
This is a non sequitur. Dynamic binding vs. static binding and public vs. private have nothing to do with the OOA/D; they are just OOPL implementation mechanisms. For example, I can implement any OOA/D model in any popular 3GL; one doesn't need an OOPL so the OOPL implementation mechanism are not relevant. The chain of functional decomposition dependencies lies in the way the methods are defined and how they are invoked.
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...
The GenericTableAlgorithm
class is just organizing the functional decomposition tree and
hard-wiring the algorithm sequence in the process() implementation
(i.e., process() is the high level node in the tree).
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. 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.
And process() itself makes a mockery of cohesion and logical
indivisibility of responsibilities. 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.
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.
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. 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).
The use of Template here is a sham. The author is using the Template /form/ for an entirely different purpose. The author has a god object and needs to organize the complex functionality. 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.
Note that for this application I could implement exactly the same method implementations with no abstract class and no inheritance. 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?
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.
For example, consider Factory Method:
[Client]
| *
|
| R1
|
| creates
| *
[Product]
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
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. 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. 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.
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.
IOW, it
takes two to tango.
Yes, but two what? In the case of the template method it is two
CLASSES bound into a single object.
To collaborate. Template needs a client. There is no reason to use the pattern if there is no complex collaboration with a client. In the paper's example there is no client object for collaboration.
In addition, with the exception of Singleton, all
the GoF patterns employ delegation and polymorphic dispatch to reify the
relationship.
That's another broad generalization. However, there is plenty of
polymorphic dispatch in the gotw article we are debating. Both the
Template Method and Strategy solution make use of it. The Strategy
example uses both.
My point, though, is that the paper example /doesn't/ use polymorphic dispatch. There is only one derived class defined so there is no substitution, which is a crucial element of Template.
[Note that Template's use of public/private methods is a form of functional decomposition. However, that is not the goal of Template. The point of Template is that there are different implementations of the private methods that are substituted based on the dynamic context of the collaboration. (Note that Template also mitigates the decomposition by limiting decomposition to one level.) If there is no substitution, then there is no point in using Template. However, the author is using it without substitution -- purely to define a functional decomposition.]
There is no delegation here and no polymorphic dispatch
because the application hard-wires exactly one implementation. IOW, the
base class is a sham.
Wrong. Look closer.
Show me a second object. Show me a second derived class.
*************
There is nothing wrong with me that could
not be cured by a capful of Drano.
H. S. Lahman
hsl@xxxxxxxxxxxxxxxxx
Pathfinder Solutions -- Put MDA to Work
http://www.pathfindermda.com
blog: http://pathfinderpeople.blogs.com/hslahman
Pathfinder is hiring: http://www.pathfindermda.com/about_us/careers_pos3.php.
(888)OOA-PATH
.
- Prev by Date: Re: Searching OO Associations with RDBMS Persistence Models
- Next by Date: Re: The wisdom of the object mentors (Was: Searching OO Associations with RDBMS Persistence Models)
- Previous by thread: Re: Template Method vs Strategy
- Next by thread: How to represent Delays in UML activity diagram
- Index(es):
Relevant Pages
|