On 12/8/05, Martin Cooper <[EMAIL PROTECTED]> wrote: > We don't have any of our own Action classes in the main app that's under > heavy development right now. It's all chains and commands. Most of our > action mappings correspond to a chain of two commands, where the first deals > with the incoming request and the second deals with preparing for the > response. In some cases, we have chunks of prep work that are reused across > multiple response types, so we break those out into additional commands.
We do much the same. We extended the Catalog so that it can be used to run Commands. When we run a Command via our exended Catalog, it utilizes a standard "preopt" Chain and a "postopt" Chain to wrap the Command (or chain) that we actually want to run, creating a request processor on the business layer. The wrapping is done by creating a new Chain at runtime, with the instant command in the middle, and executing that. No fancy wiring :) WebWork does much the same thing with Interceptors. The main difference seems to be that Inteceptors have the notion of "before" and "after" (or preopt and postopt) built in. In a WebWork application, each action can reuse or extend the default request processor ("Inteceptor stack"), or declare its own. > It's interesting to compare this approach with what we did at my last > company, before we had this handy dandy chain doodad to play with. There, I > created a request handler / display handler dual that was similar in effect > to what I described above. In some ways, Chain saved me from having to > recreate that. In my travels, I often see companies building internal frameworks to fill the role of Chain, and WebWork did much the same thing with Inteceptors. Inteceptors just beg the question, "What am I intercepting?" ::). In Chain, everything is a Command that can be composed into a Chain (or "stack"), and "interception" is a relative term. > On the other hand, the way chains handle context makes them > sometimes too flexible, making code reviews more important when the compiler > isn't doing the design checking for you. ;-) One cure for that is to extend Context and Command "in anger", with whatever design niceities you like, starting by extending execute to call your own instance of Context. public abstract boolean myExecute(MyContext context); public boolean execute(Context _context) { MyContext context = (MyContext) _context; return myExecute(context); } Context may be a Map, but it can also have whatever type-safe properties and other helper methods you might need. Here, we've extended Context to include Struts-like error-handling: * http://tinyurl.com/cgqe9 so that the business layer can report back to the presentation layer. Here, we've extended Command to support validation and persistence. * http://tinyurl.com/drzor What I've really enjoyed about working with Chain is that it's easy to catch and retain any errors. We do this by using the Catalog like a Controller and giving it an Execute method of its own. If an error occurs, it crams the message into the "Fault" property built into our Context. public void ExecuteRequest(IRequestContext context) { IRequestCommand command = VerifyRequest(context); if (context.IsNominal) { try { command.Execute(context); } catch (Exception e) { context.Fault = e; } } } Then, in a test, it's easy to check to see if an Exception were thrown public void AssertNoFault(IRequestContext context) { bool hasFault = context.HasFault; if (hasFault) Assert.Fail(context.Fault.Message); } In an application, it's just as easy to display *any* exception as a global validation error, without extra coding. > > -- > Martin Cooper -Ted. --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]