Cairngorm: Tread carefully

A popular choice of framework when working with Flex is Cairngorm which has a nice tutorial to get you started. The reason I’m writing this blog is because of some code I came across recently. A developer had read the Cairngorm guide, taken it as gospel and then implemented it exactly. The only problem is that he wasn’t writing an application for an online shop which required a shopping cart. In particular the unnamed developer should have ignored the advice on the singleton model and in particular how one implements the IResponder interface.

The Cairngorm example is simple and the singleton model binding to your mxml pages is simple – it’s the quickest way to make Flex’s asynchronous behaviour work easily. If your application is a little more complicated then this simplicity becomes highly inflexible. To make my point a little clearer look at how the result method sets properties on the model.

Scala

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

]publicfunctionresult(event:Object):void

{

varproducts:ICollectionView=ICollectionView(event.result);

varmodel:ShopModelLocator=ShopModelLocator.getInstance();

// sort the data

varsort:Sort=newSort();

sort.fields=[newSortField("name",true)];

products.sort=sort;

products.refresh();

// set the products on the model

model.selectedItem=products[0];

model.products=products;

model.workflowState=ShopModelLocator.VIEWING_PRODUCTS_IN_THUMBNAILS;

}

Ok fine. But let’s imagine a user could have more than one shopping basket (unlikely I know but just to make my point). Or even better, what if your model had separate products for his and her shops. With this pattern you’re now stuffed because the model can only handle one set of products or one shopping basket. Yes, you could change the model so it can handle multiple sets of products, multiple shopping carts but that wouldn’t be the right way to fix the problem.

To overcome this inflexibility all you need to do is ditch the singleton model. Ok maybe don’t ditch the singleton model but only store truly global properties in it – the logged in user, a list of countries etc. Now how do you get your changes from an asynchronous service call to show up in your UI? Well without sounding too radical you could just implement MVC/MVP and pass the result of the event to you controller/presenter class.

I’m not entirely sure what Cairngorm’s Command classes are supposed to do. But my Command classes do virtually nothing and I let the Controller classes manipulate any result before setting it in the view.

Scala

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

]

/** The method that is invoked on the Controller when the result method is called */

privatevar_onSuccess:Function;

publicfunctionexecute(event:CairngormEvent):void

{

_onSuccess=(event asMySpecialEvent).onSuccess;

if(ShopModelLocator.getInstance().products==null)

{

vardelegate:ProductDelegate=newProductDelegate(this);

delegate.getProducts();

}

else

{

Alert.show("Products already retrieved!");

return;

}

}

//------------------------------------------------------------

publicfunctionresult(event:Object):void

{

_onSuccess(event.result);

}

Your controller class can then set the result on your mxml page (not very MVC but no bindings required) or you can bind the mxml page to a property on your controller class (the MVC way).

Why is this more flexible? The simple reason is that you have a Controller class for every mxml page. So if you 2 shopping baskets you have 2 ShoppingBasket controller classes. If you have his and her shops you have HisOnlineShopController and HerOnlineShopController – whatever granularity works best. It also stops you using the powerful but ultimately horrendous [Bindable] property on loads of properties in one class which every mxml page is looking at. In MVC the view should only be looking for properties where it needs to be – i.e. the controller/presenter that is handling the display of data in it. That way when you’re trying to find a null reference exception you only need to look at one mxml page (plus any nested pages) and one Controller class to work out where it is. I find this much better than putting a finger in the air and hazarding a guess as to which of the million [Bindable] properties in my singleton model is being set to null and which mxml page is bound to it.

And even better ActionScript is a functional language so it’s really easy to invoke a function after an asynchronous call finishes. Java can only achieve this by using interfaces which is not quite as neat (though the flow is quite clear).