On Sat, 5 Mar 2005 20:34:06 -0800, Dakota Jack <[EMAIL PROTECTED]> wrote:

> I inquired why the Template Method pattern is being used with Commons
> Chain instead of Strategy, but never got an answer.

Back in town (for at least one day), and snipping off the flamebait
:-), it's worth philosophizing for just a coule of minutes.

Choosing between CoR, Strategy, and Template patterns is, at least
partly, a choice of the fine-grainedness you want for your abstraction
that is the basis for reusability.  In Struts 1.1/1.2, for example,
one could consider the monolithic RequestProcessor class to be an
example of the Template approach (although it mashes the abstract base
class and default concrete implementation into a single class that you
can override and specialize).  A strategy pattern approach to that
would have been to define RequestProcessor as an interface, perhaps
with a separate default implemetation, but you're at pretty much the
same point either way -- the set of steps (in the case of Struts, the
identity and ordering of the processXxx methods) is pretty much fixed,
and you've got a very coarse-grained replacement abstraction.

CoR as a pattern goes all the way to the other extreme, and tries to
make the unit of replacement as fine grained as possible.  Not only
are the individual steps small and relatively independent (the linkage
between them is knowing the keys in the context that they use to pass
information back and forth), but you are completely free to change the
order of the steps, insert new steps, or omit steps you don't need.

If you are disciplined about how you construct your steps, this can
give you a pretty significant degree of reuse.  For example, assume
you're building a CRUD type application by doing direct SQL through a
JDBC connection (whether that's a good idea or not is totally
immaterial to the illustration -- just pay attention to the philosophy
here).  One might construct individual steps like this (for an update
transaction):

Initial state - primary key of the row you're going to update is in
the context under a well-known key.  The updated fields are available
in other places (as a DTO, as a bunch of individual properties, or
whatever) that are also well known.

(a) Acquire a JDBC connection somehow, and store it in the context

(b) Based on the primary key (from the context), retrieve the current
    data for the master row you're going to update

(c) Check the timestamp in the retrieved data to make sure nobody
     else has updated the row first, while you were away

(d) Perform the appropriate updates

(e) Commit the transaction

(f) Release the JDBC connection back to where it came from.

With fine grainedness like this, you can easily reuse *portions* of
this business logic, with several variations on the theme:

* Steps (a) and (f) are reusable for any sort of transaction
  that talks to this database.  They are mutually interdependent,
  which leads to a design choice discussed further below.

* You can have an implementation of (a) and (f) that grabs
  something out of a JNDI-accessed data source for use in a
  webapp, and a separate implementation that just reuses the
  same Connection instance for a batch application, without
  the rest of the chain knowing anything.

* Step (b) can be reused for any transaction that requires
  retrieving the existing information from this particular table.
  It doesn't care what happens after it's done its work -- that
  is for the rest of the chain to determine.

* Step (c) is totally optional if you're working in an environment
  where you don't have to worry about intervening updates, so
  you could leave it out of a chain executed in that area.

* Step (d) is normally specific to the particular chain you are
  executing, but it might still be reusable if you have more than
  one business transaction that, perhaps, updates different
  subsets of all the columns in this row.

* Making step (e) separate lets you construct chains that
  update an arbitrary number of different rows in the same
  SQL transaction, without the individual updates having to
  be aware of any of the others.

In a large administrative app, you might construct hundreds of chains
like this (or define subchains that are executed via something like
LookupCommand to reduce the tedium of configuration) and have a large
degree of reuse over the set of individual command implementations. 
And those same commands can be reused again in a batch app, or on the
back end of a web service, or at the back end of a request from client
JavaScript using XmlHttpRequest, or from anything else.  As an extra
added benefit, writing unit tests for such fine grained things is very
easy ... just set up a context with the required incoming information,
execute the command, and check the results for the required
postconditions.

Think of the individual steps as LEGOs, plastic building blocks which
can snap together in all sorts of combinations, but only at certain
well-defined interface points (i.e. with some way to know what context
keys are used to pass in the input state and pass out the output
state).

The Template Method approach (and Strategy, for that matter, although
to a lesser degree) make more sense (at least to me) when you have a
set of interrelated operations that can be performed on some thing,
*and* the set of operations is the same for all potential
implementations of sets of those operations.  You also have to deal
with the impacts of changes to the interfaces to these sets (for
example, adding a method to a Java interface is likely to break all
existing implementations, unless they all happen to subclass a common
base class where you can provide default behavior.

That all being said, I'm not ever going to use things like
org.apache.commons.chain.generic.DispatchCommand or
org.apache.commons.chain.DispatchLookupCommand myself -- mixing
metaphors :-) like this gives up some of the reusability I'm after
when I choose a CoR pattern in the first place.  But I could see why
someone might want to do this, in the example above, to combine (a)
and (f) into a single source class because there is a 1:1 relationship
between the implementations of each step (for any possible overall
approach to acquiring connections).

At a pragmatic level, that's the sort of relationship I would document
in instructions to the architects assembling chains, rather than
embedding in the structure of the code.  Others may see that
differently, which is why frameworks sometimes have to accomodate
different strokes for different folks.

Craig

PS:  Pedantically, one could argue that CoR as implemented in [chain]
is one single implementation of the Strategy pattern that can be
reused by everyone -- in the pointed-at diagram of the pattern,
replace the word "Strategy" with "Command" and "AlgorithmInterface"
with "execute" :-).  But that's a discussion that doesn't lead
anywhere useful, so I won't be bothered to discuss it again.  As the
Javadocs tell you, [chain] was specifically intended to be an
implementatin of the Chain of Responsibility pattern as described in
the GoF book.

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to