explorers' club

explorations in dev, science, sci-fi, games, and other fun stuff!

A new take on SequenceCommand (part 2)

5 Comments

Now I want to get into my proposed approach. If you are just now joining us and didn’t catch part 1, you can catch up here.

Keep in mind this is still in the infant stages and I am currently developing and rethinking how this works. I certainly welcome ideas, criticism, and recommendations on my approach. So please hammer away. Onto the code….

The first step is to allow our commands a means of firing events when they do their result and fault logic. So we need to extend the EventDispatcher class. I created a base class called CommandBase that extends the EventDispatcher and implements ICommand and IResponder. So we are still maintaining the whole Command architecture in respect to Cairngorm 2.1. You could easily do this using the deprecated Cairmgorm Responder interface as well. So now every command we create just needs to extend CommandBase and override the execute, result and fault methods. But in doing so we need to make sure to call the super.execute(), super.result() and super.fault() methods as well or at least dispatch those events when overriding. Here is some sample code:


[Event (name='execute', type='flash.events.Event')]
[Event (name='result', type='flash.events.Event')]
[Event (name='fault', type='flash.events.Event')]
public class CommandBase extends EventDispatcher implements ICommand, IResponder {


public function execute (event:CairngormEvent):void {
dispatchEvent(new Event('execute'));
}


public function result (data:Object):void {
dispatchEvent(new Event('result'));
}


public function fault (info:Object):void {
dispatchEvent(new Event('fault'));
}
}

Really simple stuff and in fact it looks exactly like your regular ol’ Cairngorm Commands you have been using for quite awhile. Next we need to setup a Sequence type Command. I decided to go with SequenceCommandBase which extends…yep, you guessed it CommandBase. Why? Well, code reusage for one and also I think there may be some crazy times when you want to nest these guys. Now SequenceCommandBase has quite a bit of added stuff in order to work correctly. We have a command queue, a current index, and a command log that we can use for tracing and tracking stuff. We also have added a few more methods which I will go into detail. When I started thinking about how to decouple these sequenced commands, I came up with a few thoughts on what was needed to assist this concept:

  • a reference to the command class
  • events for the commands
  • contingency for a failed command within the sequence

Well the first point is handled pretty much the same way Cairngorm’s FrontController handles creating new events. You pass a class reference and it create a new instance of that particular class. Like in a mx.effect.Sequence we need a means of attaching child commands. I chose to use addCommand rather than addChild. I am passing a few parameters as well and assigning event handlers to the command’s result and fault events. The tricky part is how to handle events for the commands in question during the execution phase.

In a normal application of the command pattern, we just dispatch our CairngormEvent via the CairngormEventDispatcher. So events are created and almost immediately used. In our case, we are predefining the events. Normally this is not an issue. We can just create them when we add the commands and then when the commands are executed, we pass the predefined events in with it. But what happens if there are what I call data modification dependencies? Meaning what if there is a dependency on data that may be altered by a previous command’s result or failure. For example, commandTwo needs a value in the Model which currently is set to “”. And we need commandOne’s result to set that value in the Model. If we are predefining these events based on those values then we will get the original value and not the previous command’s result’s assigned value. Right now, I have a solution, but not a very elegant solution. For now I am passing an event modifier function as another parameter when we add child. These modifiers will most likely be defined in the subclasses of SequenceCommandBase.

Next we need to execute each command. I called it executeCurrentCommand. Pretty original huh? Basically it does a few things. It does what it says namely, but before doing so it also does the following:

  1. checks to see if we have an event,
  2. if not creates a generic CairngormEvent,
  3. sees if there is any needed modification to the non-generic event if we passed in during addCommand,
  4. creates an instance of the current command class (remember which will be a subclass of CommandBase).
  5. executes the current command and passes the current event as the parameter.

So you might be thinking, isn’t that what the FrontController does? Yes. But unfortunately since the FrontController subclasses create new instances of the needed commands on the fly, we can’t really tap into that functionality. So right now the SequenceCommandBase is also subbing as a mini-FrontController.

The next set of methods are really event handlers for the current command’s result/fault events. But they do handle the contingency logic for what happens if something fails. But first I should explain the command queue a bit further. It not only stores the commands, it also stores references to their respective events, event mod functions, and the boolean values as to handle what to do on failures. So when the current command fails or succeeds then the respective event chimes in and says, “well if he succeeded then go on to the next command, but if he failed then we need to follow his contingency booleans and execute the logic to see if we proceed or blow chunks.”

So I guess this would be a really good time to start pasting more code. Okay here is a sample of the addCommand method:

public function addCommand (commandClass:Class,
event:CairngormEvent = null,
eventModificationLogic:Function = null,
continueOnFaultFail:Boolean = false,
jumpOnFaultFail:Boolean = false,
jumpIndex:int = 0):void {

//is there a more elegant way of handling this?
var command:CommandBase = new commandClass();
command.addEventListener(‘result’, onResult_currentCommandHandler);
command.addEventListener(‘fault’, onFault_currentCommandHandler);

_commandQueue.push([command, event, eventModificationLogic, continueOnFaultFail, jumpOnFaultFail, jumpIndex]);
}

Here is a glimpse of what is currently the logic behind the execution:

protected function executeCurrentCommand ():void {
//checking to see if we need to update the event before executing the currentCommand
if (_commandQueue[_currentIndex] [SEQ_EVENT_MOD_INDEX]){
var func:Function = _commandQueue[_currentIndex] [SEQ_EVENT_MOD_INDEX] as Function;
}

var currentCommand:CommandBase = _commandQueue[_currentIndex] [SEQ_COMMAND_INDEX] as CommandBase;
var commandEvt:CairngormEvent;

if (_commandQueue[_currentIndex] [SEQ_EVENT_INDEX] == null){
commandEvt = generateGenericEvent();
} else {
commandEvt = _commandQueue[_currentIndex] [SEQ_EVENT_INDEX] as CairngormEvent;
}

currentCommand.execute(commandEvt);
}

And finally here is some quick code for the handlers :

protected function onResult_currentCommandHandler (evt:Event):void {
updateLog(‘RESULT’);
_currentIndex++;
executeCurrentCommand();
}

protected function onFault_currentCommandHandler (evt:Event):void {
var allowPassThru:Boolean = _commandQueue[_currentIndex] [SEQ_ALLOW_CONTINUE_INDEX] as Boolean;
var allowJumpTo:Boolean = _commandQueue[_currentIndex] [SEQ_ALLOW_JUMP_INDEX] as Boolean;
var jumpTo:int = _commandQueue[_currentIndex] [SEQ_JUMP_INDEX] as int;

if (allowPassThru){

if (allowJumpTo){
updateLog(‘FAULT JUMP’);
_currentIndex = jumpTo;
} else {
updateLog(‘FAULT PASS’);
_currentIndex++;
}

executeCurrentCommand();

} else {
updateLog(‘FAULT TERMINATE’);
fault(_commandLog);
}
}

I apologize for the fact I don’t have a REAL blog where I can format the code. Maybe here shortly I will, but for now I am stuck with the free online blog @ wordpress.

Anywho. Let me know some of your thoughts. Again, keep in mind that this is just a proof-of-concept in progress and will evolve.

Thanks ,
Jwopitz

Advertisements

5 thoughts on “A new take on SequenceCommand (part 2)

  1. This is really cool. Can you make it available for download and provide an implementation example?

  2. Hi Mike,

    Thanks for the comment. Right now I have been swamped by work, finding a new contract and many other issues. So I have had no time to really follow up on it. At some point hopefully in the next 2 weeks, I plan to really tackle some of my own pet-projects including this one. Please check back then to see if I have made any progress. And also feel free to ask my any questions. Remember, this new take on the SequenceCommand is still a fuzzy little nugget in my head. Very amorphous at this point still.

    – Justin

  3. Could you post code sample?

    Thanks.

  4. Hi everyone. What should I do when my credit card expires? I’ve received a new credit card offer in the mailbox which is the replacement of the expired one. But the features seem to be different. Do I risk accepting the offer? I think that it is better to go to a web resource and make an application there. Quite a good place to do it is

    rebuild ur credit

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s