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]

Reply via email to