This topic made the rounds amongst my friends and family, so I thought I’d share with the remaining few people on the Internets who actually read my blog what my essential list is.
My intended purpose for the AbstractCommand class in Cairngorm was two-fold:
Batch Commands | Abstract Events | Abstract Delegates

Here’s an example Command in my system, it extends AbstractCommand and implements the ICustomCommand interface – which simply enforces a commandSuccess() and commandFault() method be present.
When the execute() method is invoked, the callback function property of the domain event is assigned by calling the setCallbackFunction() of the AbstractCommand, then the standard process of instantiating the proper delegate happens.
Now that the Command has a reference to the callback function (if it exists), in the commandSuccess() method, we simply call the AbstractCommand’s notifyCaller() method which executes that function.
package business.commands
{
public class DeactivateProjectCommand extends AbstractCommand implements ICustomCommand
{
private var _model:ModelLocator = ModelLocator.getInstance();
public function execute(event:CairngormEvent):void
{
setCallbackFunction((event as ProjectEvent).callbackFunction);
var delegate:ProjectDelegate = new ProjectDelegate();
delegate.setResponder(new Responder(commandSuccess, commandFault));
delegate.deactivate((event as ProjectEvent).eventData);
}
public function commandSuccess(event:ResultEvent):void
{
if (event.result.success)
{
notifyCaller(event.result.data.project as Project);
}
}
public function commandFault(event:FaultEvent):void { }
}
}
This class is straightforward and simple. It simply contains the setCallbackFunction() and notifyCaller() methods with a private class member _callback.
package business.commands
{
public class AbstractCommand
{
private var _callback:Function = null;
public function setCallbackFunction(value:Function):void
{
if (value != null) _callback = value;
}
public function notifyCaller(info:Object = null):void
{
if (_callback != null)
{
if (info != null)
{
_callback.call(this, info);
} else {
_callback.call(this);
}
}
}
}
}
This interface simply enforces the implementation of the commandSuccess() and commandFault() methods.
package business.commands
{
import com.adobe.cairngorm.commands.ICommand;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
public interface ICustomCommand extends ICommand
{
function commandSuccess(event:ResultEvent):void;
function commandFault(event:FaultEvent):void;
}
}
My intended purpose for the AbstractEvent class in Cairngorm was three-fold:
Abstract Commands | Abstract Delegates
To accomplish my goals, each domain event (e.g. ProjectEvent, PaymentEvent, etc.) would be polymorphic (kinda) in that there is no longer just one identifier designated with the EVENT_ID constant. In addition, I wanted to abstract as much functionality and classification as possible into a common class that each domain event would extend.

Here’s an example of a domain event. It has three public identifiers – CREATE_PROJECT, LOAD_PROJECTS, DEACTIVATE_PROJECT- and has no logic other than to pass its constructor’s arguments to AbstractEvent.
package business.events
{
import com.adobe.cairngorm.control.CairngormEvent;
public class ProjectEvent extends AbstractEvent
{
public static const CREATE_PROJECT:String = "createProject";
public static const LOAD_PROJECTS:String = "getProjects";
public static const DEACTIVATE_PROJECT:String = "deactivateProject";
public function ProjectEvent(type:String, data:Object = null, callback:Function = null)
{
super(type, data, callback);
}
}
}
The AbstractEvent class itself isn’t much more complicated at all. It simply has three public variables to hold the type of the event, an object containing key/value pairs for the data, and the reference to the callback function in the view (if specified).
package business.events
{
import com.adobe.cairngorm.control.CairngormEvent;
public class AbstractEvent extends CairngormEvent
{
public var eventType:String = "";
public var eventData:Object = new Object();
public var callbackFunction:Function = null;
public function AbstractEvent(type:String, data:Object = null, callback:Function = null)
{
super(type);
eventType = type;
eventData = data;
callbackFunction = callback;
}
}
}
Here’s some snippets of code from my project view. One piece of functionality is for a user to disable a selected project from a list in an AdvancedDataGrid. When the user clicks on the “Disable Project” button, the btnDeactivateProject_Click() function fires and creates a new Project event in which the loadProjects() function is specified as the callback function.
private function btnDeactivateProject_Click(e:Event):void
{
var p:Project = gridProjects.selectedItem as Project;
var event:ProjectEvent = new ProjectEvent(
ProjectEvent.DEACTIVATE_PROJECT,
{project:p},
loadProjects);
event.dispatch();
}
public function loadProjects():void
{
var projectEvent:ProjectEvent = new ProjectEvent(ProjectEvent.LOAD_PROJECTS);
projectEvent.dispatch();
}
<mx:Button id="btnDeactivateProject"
click="btnDeactivateProject_Click(event)"
x="10" y="393"
label="Deactivate Project"/>
Your controller logic doesn’t change. The only difference is that you’re using one domain event, with specified identities, mapped to difference Commands.
package business
{
import business.commands.*;
import business.events.*;
import com.adobe.cairngorm.control.FrontController;
public class Controller extends FrontController
{
public function Controller()
{
super();
addCommand(ProjectEvent.LOAD_PROJECTS, LoadProjectsCommand);
addCommand(ProjectEvent.CREATE_PROJECT, CreateProjectCommand);
addCommand(ProjectEvent.DEACTIVATE_PROJECT, DeactivateProjectCommand);
}
}
}
My intended purpose for the AbstractDelegate class in Cairngorm was two-fold:
Abstract Commands | Abstract Events

By extending the AbstractDelegate class, each domain Delegate created by a developer become absurdly lightweight and simple. Basically, it become a series of methods mapped to remote procedure call names.
package business.delegates
{
public class ProjectDelegate extends AbstractDelegate
{
public function loadProjects():void
{
send("getProjects", {});
}
public function deactivate(eventData:Object):void
{
send("deactivateProject", eventData);
}
public function createProject(eventData:Object):void
{
send("createProject", eventData);
}
}
}
The the AbstractDelete class will take care of holding the reference to the application’s data model, and constructing the final RPC with the designated Responder attached.
package business.delegates
{
import business.RemoteFactoryLocator;
import flash.utils.getQualifiedClassName;
import mx.rpc.IResponder;
public class AbstractDelegate
{
private var _responder:IResponder;
private var _remoteService:RemoteFactoryLocator = RemoteFactoryLocator.getInstance();
public function AbstractDelegate()
{
if(flash.utils.getQualifiedClassName(this) == "business.delegates::AbstractDelegate")
{
throw new Error("ClassInstantiationException: Cannot create an instance of an abstract class.");
}
}
public function setResponder(responder:IResponder):void
{
_responder = responder;
}
public function send(eventName:String, eventData:Object):void
{
_remoteService.send(eventName, eventData, _responder);
}
}
}
So now all of my delegates extend the AbstractDelegate which allows us to write less code for all of these stinking delegates for our application.
The only ramification is that the Commands (see Abstract Command post for example) now have to call the setResponder() method on each delegate.
As we continue to transition from HTML/Javascript user interfaces to Flex ones here at work, we continue to go through the expecting growing pains when learning any new technology. From the simple (“How on earth do I specify a custom tooltip? oh…), to the complex (“If we provide the ability to move this control within the panel, how do we get the other controls to adjust themselves accordingly? oh….”), we’ve had many AHA! moments in the past few months.
Luckily, we are all very seasoned application developers, and there have only been a few problems to which we’ve been unable to apply past experiences and overcome quickly. Our expertise is growing quickly, and I can see in my head a year from now, after we’ve all built our collective intellectual base of knowledge of Actionscript and MXML, what amazing things we’ll be able to accomplish as we move beyond the mundane tasks of figuring out the intricacies of a ViewState, to being able to conceptualize the application as a whole and how it fits into the business needs.
It’s been amazing to see the difference at how quickly a new technology can be learned and implemented when working with a team of developers who are advanced and experienced , as opposed to a smart, talented novice.
It’s that vast accumulation of failures and successes and stored design patterns and workarounds that we all build during our careers that can significantly reduce the cost of a technology transition. I remember reading an article not long ago from someone espousing that experience – while definitely a factor – is not one of the major factors when looking to hire new developers. I’ve always disagreed with that. Someone with 10 or more years experience is likely able to learn technology faster, see hurdles ahead and how to avoid them, and generally see the big picture better than a greenhorn.
So the growing pains continue, but they hurt less and less each day.