views:

171

answers:

8

Hello I am trying to build a framework of IAction objects that will execute in a sequence. Each IAction implementation will execute its processAction() method as it was implemented for. This method returns a IAction, which in most cases is itself, but in some cases may be a pointer to another IAction in the List. The IActionIterator interface is created to manage that movement in the List. Here is the interface



public interface IAction {
    public IAction processAction();
}

pulbic interface IActionIterator {
    public IAction getFirstAction();
    public IAction getNextAction( IAction action );
}

My framework will obtain a List and will loop thru the list executing the processAction() of each IAction class. Here is what the loop will look like



  IActionIterator  iter = ... // created some how
  IAction action = iter.getFirstAction();
  do {
       IAction newAction = action.processAction();
       if( action.equals( newAction ) 
           action = iter.getNextAction( action );
       else 
           action = newAction;

  while( action != null  )  {

So each IAction has its implementation to execute and some IAction have business logic that will return an IAction in the list instead of executing the next one in the list.

I am anticipating some IAction classes that will execute, but the next IAction in the list will need the results from the first. For example one of the IAction is executing an SQL query and the results are pertinent to the next IAction in the list.

So my question is how would or should I implement this in information passing form IAction to IAction in my designed Framework?

+1  A: 

Change the return signature of your getFirstAction() / getNextAction() to be a simple holder object:

public class IActionResponse {
    List getResultList();
    IAction getReturnAction();
}
matt b
+1  A: 

I have never done this, but perhaps you can use the Serializable interface to pass generic information between Actions? Then the Actions would expect certain kinds of data and know what do to with them.

Ben Goosman
+1  A: 

Hope this helps.I would put it in the interface itself...if only result of previous action is needed otherwise if the result of ancestors also is required then better store the results in some external class instance that gets populated in the cycle and based on the action id you can pull the results..

For a simpler case where result of previous action is needed

IActionIterator { public IAction getFirstAction(); public getFirstActionResult(); public IAction getNextAction( IAction action ); }

Rajat
+2  A: 

Sounds like you are trying to represent a state-transition graph. Can you use an FSM rather than trying to roll your own?

ykaganovich
+1  A: 

This is the kind of circumstance where the following sort of interface does really well:

public interface IAction {
    void invoke(IActionRequest req, IActionResponse res);
}

Each action gets its inputs -- which are open-ended, could be anything -- from the 'req' object. In turn, each action has the chance to communicate outputs -- again, open-ended -- using the 'res' object.

And FWIW, the framework you're describing sounds very similar to some existing frameworks. Check out the Cernunnos project for an example of one that's designed pretty close to what you're proposing.

Drew Wills
Hello; I looked at the Cernunnos project and downloaded the source code to get some ideas on the implementation. It is educating me on how to implement my small framework. Thanks for the pointer.
Peter Delaney
+1  A: 

You could consider simply passing the actions a context/state object (which is essentially a map). Have the action populate the context object with the elements that it needs to pass onto other actions along the chain. Subsequent actions can then use those elements and manipulate the context as necessary. You need to make sure that you sequence things correctly as this is like managing global state. You may need to have multiple interfaces that only expose a subset of the context to particular actions. Your controller can examine the context between action invocations as necessary.

Trevor Tippins
+1  A: 

I think what you need is the combination of two design patterns: Command and Chain of Responsibility.

In commands you can encapsulate parameters for actions and pass data between actions. You can record processing of parameters and even achieve an undo functionality if needed. In fact, each command object will be something like a context for the current processing of actions.

Actions can pass control to each other using chain of commands, where each action has link to the next action. After doing its business logic an action calls next action to process providing command object to it.

To make it expandable good to different action classes you can also use pattern Template method when implementing chain of responsibility. Thus each action class will be a subclass of some super action that will define a template method where some pre-processing of command will take place, like checking arguments and other stuff. Then the sub-class will be called and after that template method will pass control to the next action in the chain.

When using such patterns you will not need a list of actions and a general loop to process them. You just construct a chain of actions and then call process method on the first action in a list providing a command object to it. Also, when constructing a chain you also have additional freedom to mix the order of actions processing the way you want it. There will be no dependency on the order of actions in a list.

So, basically you'll need next classes and interfaces:

public interface IAction {
     public void process (Command command);
}

public class SuperAction implements IAction {
     private IAction nextAction;

     public void process (Command command) {
           // Check command object

           processAction(command);

           if (nextAction != null)
                nextAction.process(command);
     }

     public abstract processAction(Command command);

     public void setNextAction(IAction action) {nextAction = action;}
}

public class ActionConstructor {
     public IAction constructActoinChain() {
           // construct chain and return first object
     }

}

And to complete this, you'll have to define Command object, either with execute method or not. If without execute method, then Command will just be a container of parameters that are passed between actions.

Yuriy Tkach
A: 

Some general remarks: you are creating a finite state machine, which could be the beginning of a workflow engine. It's an idea to look a bit at other peoples' attempts:

Workflow engines often operate on a standard type of workflow item. If this is the case for you, then you could pass a (collection of) workflow items (objects implementing your WorkflowItem interface) into the first action, and pass it on to each next action. This allows the engine to treat common workflow concerns by referring to the WorkflowItem interface. Specific manipulation would require detecting the subtype.

One topic I have not heard anybody about here is a typical aspect of workflows: asynchronous processing. Often you want to be able to perform actions up until a certain point and then persist the workflow state. Then the workflow could be triggered to continue by some event (i.e. a user logging in and making a decision). To do this you would require a data layer which can persist the generic workflow state separately from the application-specific data.

I recently built a full-fledged workflow engine for a project I was working on. It is doable (especially if you don't try to make it endlessly generic, but match it to the problem at hand) but a lot of work. The reason we decided to do this was that the existing frameworks (jBPM et al.) seemed too unflexible and heavy-handed for our needs. And I had a lot of fun doing it!

Hope this helps.

Adriaan Koster