explorers' club

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

Debunking some Cairngorm myths

9 Comments

preface

Let me preface this by saying that I am not a fanboy of anything.  I am a pragmatist and as such seek to improve myself by utilizing, combining and discarding every skill, tool, etc. made available to me.  I have used Cairngorm ever since the Flex 1.5 days and have had a love/hate relationship with it ever since.  That being said I still favor Cairngorm in most situations.

For about the last year and a half, a few competitors to Cairngorm have popped up: PureMVC, Swiz, Mate to name a few.  All offer innovative approaches and adderss some of the much-needed features Cairngorm lacks.  I have dabbled in both PureMVC and Mate.  Though I appreciate the efforts put forth by both frameworks I am still a fan of Cairngorm for most situations.   That is not to say that I don’t mix and match as you will soon see.

view helpers without the coupling:

problem

One big argument I have seen in some of the Mate posts/comments is that Cairngorm sucks at providing a means to get back to a view.   It is true that in most Cairngorm examples, the cycle of a particular User eXperience (UX) is that a view triggers an event, the event triggers a command, the command’s response triggers an update to the model and via {binding} the model triggers a change in the view.  This is where the whole “boiler plate” argument is generated and those opponents are right in that you now have a view that is coupled to dependencies on the model and relevant cairngorm event(s).

solution

One of the most overlooked features of Cairngorm is the idea of data payloads.  All CairngormEvents have a data property as a generic object.  In most cases people use this as a single property such as an argument used for a service API.  Rather than using the data property as a flat, single use object, why not use it as a multi-purpose object.  For example:

//a particular UX triggers this
var evt:CairngormEvent = new CairngormEvent("GetSomethingFromSomewhere");
evt.data = {};
evt.data.params = //whatever you are looking to send as service API parameters
evt.data.callback = someFunc; //this points to a function on your view

//the callback signature
private var someFunc (... args):void { /* do something */ }

In the command you would simple store the data object for the life of the command like so:

//stored inside the main part of the command for later use
var vo:Object;

//during the command's execute method
vo = event.data;

//during the response you could do something like so
var func:Function = vo.callback as Function;
func.call(null, responseParam0, responseParam1, etc...);

As you can see you need not couple your views to your command or visa versa.  Using some old school AS2 techniques we can trigger a response to a result in a view, completely bypassing the need to update a central model or use {bindings}.  This also addresses the whole argument about the “monolithic model” in Cairgorm.  Now some purists may argue this is bad OOP or that this is not a view helper type solutions.  Whatever man…. I am not here to preach or argue semantics, just be pragmatic.  But keep reading as I will show another neat trick in case this hasn’t convinced you.

borrowing the mediator pattern from PureMVC:

problem

In most standard cases a view in a Cairngorm app has data dependencies on the model via {binding} (which the previous tip can help with) and it needs to know what Cairngorm events to dispatch based on a UX.  Coupling and more coupling.  Why not use a mediator.  Let’s borrow from PureMVC’s mediator pattern.

solution

So instead of building one-time-use views, you can create bigger views in a more componentized way.  Use a mediator.  For those not familiar with how this works, the idea is you have a class that wraps around a particular view.

public function SomeMediatorClass (target:SomeViewClass)
{
   super();
   target.addEventListener("someViewEvent", handler1);
   target.viewcomponent.addEventListener("anotherViewEvent", handler2);
}

private function handler1 (evt:Event):void {}
private function handler2 (evt:Event):void
{
   var cEvt:CairngormEvent = new CairngormEvent("someCommand");
   cEvt.data = {};
   cEvt.data.params = ....
   cEvt.data.callback = callback0
   cEvt.dispatch();
}

//remember this
private function callback0 (... args):void {}
private function callback1 (... args):void {}

Here are a few ways to implement a mediator.  One is specific to the application-level class which requires instantiation within the view, the other show subsequent applications of the mediator and their related views:

//for application-level class
public var mediators:Array = [];

private function initializeHandler ():void
{
   mediators.push(new ApplicationMediator(this));
}

//adding your main UI for the application once certain assets are loaded
//within the applicationMediator
private var view:MyApplicationClass
public function ApplicationMediator (target:MyApplicationClass)
{
   view = target;
   view.addEventListener(FlexEvent.APPLICATION_COMPLETE, target_applicationCompleteHandler);
}

private function target_applicationCompleteHandler (evt:FlexEvent):void
{
   var mainGUI:MainGUIClass = new MainGUIClass();
   addChild(mainGUI);

   //storing to the already existing array of mediators on the application-level class
   view.mediators.push(new MainGUIClassMediator(mainGUI));

   //or you could just store it here within the mediator itself
   this.mediators.push(new MainGUIClassMediator(mainGUI));
}

The mediator IS coupled to the view however the view is completely ignorant of the mediator (in most cases).  The mediator registers listeners for particular events that the view dispatches.  Based on those UX events, the mediator can execute logic such as dispatching CairngormEvents on behalf of the view.  Utilizing the callback tip earlier you can completely abstract out the view from your application implementation.  If your view implements the IDataRenderer interface then you can assign response objects to the data property on the view, and if you must, allow {binding} within the view on its data property to update its constituents.  Basically the mediator facilitates the implementation of your view in the sense of the UX.

More to come

Advertisements

9 thoughts on “Debunking some Cairngorm myths

  1. Hi,
    have you considered simply not using a framework. I mean, Flex is already a framework in itself. I posted on this recently http://arielsommeria.com/blog/2009/02/10/framework-overload/

  2. Yes I have considered the anti-framework approach. The issue with that is generally the fact that if you are working with a larger set of developers, or you have a newcomer start in the middle of the project, depending on how “elaborate” your delegation of responsibilities is, you might be setting a steeper learning curve with your code base.

    The other issue with the anti-framework approach is generally a matter of scalability. Smaller projects might get away with this, but when you start to talk about enterprise-level applications, having an established framework that has passed a certain level of refinement helps make scaling an application go alot smoother.

    Back to the real gist of my post tho, I really think a pragmatic approach is best. So if for a given project, the pragmatic thing to do is to NOT use a framework, then I am all for it.

  3. Ololo! I like what is written here!!

  4. I am impressed. I am just impressed…

    I have a question however (coz i do not use mate), we do you place the mediator… coz its looking like a front controller in cairngorm.

    I largely support ur perspective on being pramatic.

    Cld u further expose ow handle other naughty issues like (modules) end-to-end?

    Once again, im impressed! Nice one!

    Thanks.

  5. @Seun

    There are many ways to utilize the mediator within Cairngorm. Generally I try to avoid placing them within the views. One notable exception is the application-level class. I simply add the mediator(s) to an array on the application-level class during the initialization phase. All subsequent mediators can then be handled and stored thru a mediator higher up on the “mediator display hierarchy” (for lack of a better term). I updated some of the code above so you can see this.

    As for modules, I haven’t gotten to far into that issues because most of the modules I have created are built exclusively for a shell application. This whole Cairngorm + PureMVC + IoC thing is still a work in progress.

    Glad you like the post tho.

  6. Very well written post.
    You know what you’re talking about.

  7. Absolutely true! I’ve come to the exact same practices on my own 🙂

  8. great post.

    I am also pragmatist as you and I usually prefer using Caringorm, but mostly since it’s Adobe’s recommended framework and I believe it’s better to stay on the main road then driving on a side road. With that said, I think it’s important to keep things in prospective and decide the best framework based on team’s skills as well as technologies used. For instance, I encoruge using PureMVC in a large media company, since they have JS, Pure AS, Flex and iPhone developers and PureMVC framework is supported by all these languages.

    I see the lack of separation of the logic and view in Cairngorm as a missing piece in Cairngorm especially with the release of Flex 4 and Catalyst which promote a design centric approach where the logic is separated from the view.

    The mediator in PureMVC is basically a presentation model design pattern implementation.

    There are few more approaches to use a presentation model in Caringorm. For instance, here one approach I implemented back in Dec 08:

    http://elromdesign.com/blog/2008/12/13/cairngorm-and-the-presentation-model-using-ant-scripts/

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