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]