PILGRIM, Peter, FM wrote:

-----Original Message-----
From: Jing Zhou [mailto:[EMAIL PROTECTED]

----- Original Message ----- From: "PILGRIM, Peter, FM" <[EMAIL PROTECTED]>
To: "'Struts Developers List'" <[EMAIL PROTECTED]>
Sent: Monday, October 13, 2003 5:39 AM
Subject: RE: Struts-chain Behavior Discussion





*Question of Architecture*

Does this mean that we have solved the hideous Action

Chaining debate?


In theory an Struts Action could be refactored to be a type of ``Command'' and therefor Actions could be chained.


Whenever there is a debate and each side seems to be very reasonable
in certain contexts, it is more likely the system itself is in-complete or
fails to meet the requirements. We use Controller Delegation Model
and/or Event Handler(s) to deal with such problems.





What are you talking about here? What I am talking about or rather
aiming the ability create common Struts components.


Suppose team T1 invents a generic web search engine component.
Let say another team T2 wanted to use that component, say
embed it is in own JSP views using say Tiles.
How does the search component forward or transfer control to the external component that T2.


One way could be to use an action chain. First, one writes a Struts
Action in such way that it puts a known public bean or put the bean
in the form bean. Whatever. This Output Bean contains contains
your search result or selected object. Second, one can generate a dynamic ActionForward that names the second Action
to forward.


But I am thinkgin Craig has given us an alternative option, of maybe
using a Command to do this. But if and only if an Action is refactored
as a Command.



It sort of is in the existing proof of concept (i.e. there is an adapter Command that can call an existing Action), but that will be of more interest to people with existing code that want to use it in a CoR based request processor. I'm imagining that new code based on such a pattern would be constructed out of Commands in the first place.




Commons-chain is not intended to solve that debate topic originally.
Last year, people found when they have two RequestProcessor(s), say
A and B, if they need to design a new RequestProcessor C that has
methods from both A and B, the best they can do is to let C extend
one of them, say A, and then copy the methods (possibly line by line)
from B. So a configurable RequestProcessor is somehow needed
to reuse portions of several RequestProcessor(s). This is what
commons-chain is intended to solve initially.




Ay Ay, Captain! I don't mean to rain on the parade. I was involved
in the composable request processor discussion way way back in the
summer. As the Expresso core committer who integrated Struts 1.1 I found also a futuristic design issues merging the TilesRequestProcessor and our own ExpressoRequestProcessor.
How would you merge the next processor, and the next one, and
the next one after that.




But the problems have not been cleanly solved in regard to reusability.
When a Command is deeply coupled with other Command(s) in a
chain through one or more flags/states in Context (say cancel flag,
invalid token flag, invalid validation flag, etc.) its reusability in a different chain is very low in theory or very difficult to use (you need
to configure many flags to get it work and subject to the condition
the new Context offers the same set of flags).





I dont understand the reusability problems. To me having
read the Gang of Four original book, the design of Chain
of Responsibilty and Craig's code and what he has said
to me (to us) on list makes this a non-issue. Actual design
pattern is a linear path. The only difference I can see is the context, which I guess you should treat it as opaque object any way.


If you write a Command that need various context setting
that you should declare in the javadoc or documentation.



The code I've written to date illustrates how I propose to reduce problems of interdependence between commands. For every case where your command expects an input attribute in the Context (or plans to create an output attribute), have a String property on the Command's implementation class made up of the attribute name with "Key" on the end, that documents the name of the attribute you intend to use. This does several things for you:


* It self documents the appropriate attribute keys if you are
 diligent about applying this design pattern.

* It makes the actual keys to be used configurable (improves
 reusability in different environments).

* It means that your code can *ask* a Command instance where
 it stored something, or plans to store something, instead of
 just assuming.

For example, the Command that selects an appropriate ActionConfig based on the input path (the Command version of the processMapping) needs to store the ActionConfig it found under an agreed-upon location in order for later commands to find it. Snippets of this class (AbstractSelectAction) include:

public abstract class AbstractSelectAction implements Command {

       private String actionConfigKey = Constants.ACTION_CONFIG_KEY;
       public String getActionConfigKey() { return (actionConfigKey); }
       public void setActionConfigKey(String actionConfigKey) {
           this.actionConfigKey = actionConfigKey;
       }

public boolean execute(Context context) throws Exception {
...
ActionConfig actionConfig = ...; // Look up the correct ActionConfig instance
context.put(getActionConfigKey(), actionConfig); // Store under the specified key
return (false); // Let the chain continue
}


Note also the use of constants to set the default value across all the commands that have an "actionConfig" property.

One of the core values of decomposing things into chains, however, relates to unit testing -- an individual command does not care *who* created its input parameters or *how* they got built. It only cares that they exist at the right time. Thus, it is much easier to set up unit testing frameworks for your fine grained commands: set up some input objects (mock objects if needed, like a MockServletContext), call the command's execute() method, check the return value for what is expected, and check the Context to observe the state changes that the Command was supposed to have created.




I did a little exercise to reduce the command coupling degree by
introducing a NestCommand. Like LookupCommand to execute
a specified chain, NestCommand always return false regardless
what nested chain returns. So a termination of nested chains does
not force a termination of calling chains when NestCommand
is used. The following picture illustrates the idea:




You said your NestCommand always returns `false'




Main Chain:
NestCommand
        Nested Chain:
        NestedCommand1
        NestedCommand2
        NestedCommand3
LastCommand

If any nested commands return true, the control flow will
*jump* to LastCommand. It seems to give an answer to my
original question without the notion of branch behavior :-)




And this paragraph NestCommand can return true. This conflicts
with the first. What if NestCommand return false does that
not terminate the chain, and thus stop LastCommand from executing.




NestedCommand2 does not need to check any flag set by
NestedCommand1 in this way and NestedCommand3 does
not need to check any flags set by NestedCommand1 and 2.
They could be reused in other chains without concerning about
flags.





It sounds to me like you trying to create conditional commands.


A
|
B-D-E
|
C

If B is a command that has IF THEN behaviour that is on a boolean flag V
then


if V == true then the order of execution is A,B,D,E,C
(the execution terminates after C)
otherwise V == false then order of executuion A,B,C
(the execution terminates after C)

As Craig said, he wants to remain in the original linear CoR design pattern. I can really the problem is that branching leads to different conditional behaviours.

Let's consider SWITCH behaviour. If node B is a true branch then

if V == true then the order of execution is A,B,D,E
(the execution terminates after E)
otherwise V == false then order of executuion A,B,C
(the execution terminates after C)


I can see why he doesn't want to get it to this, because it out-of-scope
for what the original design aims were.



To be clear ... Jing, or anyone else who needs it, is welcome to build their own Command implementations that do things like this. It's just that explicit support for conditional behavior does not belong in the fundamental commons-chain APIs.


--
Peter Pilgrim,
Struts/J2EE Consultant, RBoS FM, Risk IT
Tel: +44 (0)207-375-4923



Craig




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



Reply via email to