Jul/090
Cairngorm Super Classes
I’m a big fan of the Cairngorm micro-architecture for Flex, I am unapologetic and vocal about my support of it. I’m also incredibly lazy, and hate to rewrite the same two or three lines of code over and over, but I found that because of some of the design choices that were made on two of my recent projects, I was having to do just that. The biggest example of my rewriting the same code was in my commands. If you use Cairngorm, and you’re anything like me, you probably have commands that look roughly like this:
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 27 28 29 30 | package com.softwarejesus.cairngorm.command { import com.adobe.cairngorm.commands.Command; import com.adobe.cairngorm.control.CairngormEvent; import mx.controls.Alert; import mx.rpc.IResponder; import mx.rpc.events.FaultEvent; public class SimpleCommand implements Command, IResponder { public function execute(event:CairngormEvent):void { /*Do something - call a web service*/ } public function result(data:Object):void { /*Handle the response*/ } public function fault(info:Object):void { var event:FaultEvent = info as FaultEvent; Alert.show(event.fault.faultString); /*trace(event.fault.faultString);*/ } } } |
So I decided I was going to run with this lazy thing and create a simple class called MyResponder which would at least remove the need to re-implement the fault method again and again. But then I noticed another pattern, but before I can show you I need to explain a little bit about how applications were structured. These applications allowed the user to open up 1 to n datagrids or charts. So I created a simple multiton model which contained data that controlled how the data was retrieved, how the data was rendered, as well as the actual result data. Here’s what models basically looked like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | package com.softwarejesus.cairngorm.model { import mx.collections.ArrayCollection; [Bindable] public class GenericModel { /*Criteria variables*/ public var string1:String; public var date1:Date; /*Display variables*/ public var string2:String; public var number1:Number; /*List of some type of objects*/ public var resultData:ArrayCollection; } } |
When somebody launched a new chart or datagrid a new GenericModel was created. It was then populated with the data my app needed to render it properly, search criteria, and then a new Cairngorm Event was created to retrieve the applicable data. Depending on how the data was structured, I would have a number of events, like this one:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | package com.softwarejesus.cairngorm.event { import com.adobe.cairngorm.control.CairngormEvent; import com.softwarejesus.cairngorm.model.GenericModel; public class SpecificEvent extends CairngormEvent { public var obj:GenericModel; public static const EVENT_TYPE:String = "SpecificEventType"; public function SpecificEvent(obj:GenericModel) { super(EVENT_TYPE); this.obj = obj; } } } |
I would also have a number of commands that were structured like this:
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 27 28 29 30 31 32 33 34 35 36 37 38 39 | package com.softwarejesus.cairngorm.command { import com.adobe.cairngorm.commands.Command; import com.adobe.cairngorm.control.CairngormEvent; import com.softwarejesus.cairngorm.event.SpecificEvent; import com.softwarejesus.cairngorm.model.GerenicModel; import mx.collections.ArrayCollection; import mx.controls.Alert; import mx.rpc.IResponder; import mx.rpc.events.FaultEvent; import mx.rpc.events.ResultEvent; public class SpecificCommand implements Command, IResponder { var obj:GerenicModel; public function execute(event:CairngormEvent):void { var newEvent:SpecificEvent = event as SpecificEvent; this.obj = newEvent.obj; //get my delegate //make some service call } public function result(data:Object):void { var event:ResultEvent = data as ResultEvent; obj.resultData = event.result as ArrayCollection; } public function fault(info:Object):void { var event:FaultEvent = info as FaultEvent; Alert.show(event.fault.faultString); } } } |
A repeated pattern is an opportunity to be lazy, which I’m a huge fan of. What I came up with were 2 super classes that I would use to cut a great deal of superfluous, repetitive code out of my project. The first super class I’ve created is my SuperEvent, you’ll notice that it implements EventConstants. EventConstants contains all the event type strings that I use in my project:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | package com.softwarejesus.cairngorm.event { import com.adobe.cairngorm.control.CairngormEvent; import com.softwarejesus.cairngorm.model.GerenicModel; public class SuperEvent extends CairngormEvent implements EventConstants { public var object:GerenicModel; public function SuperEvent(object:GerenicModel, type:String) { this.object = object; super(type); } } } |
Next came my SuperCommand,
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 27 28 29 30 31 32 33 34 35 36 37 | package com.softwarejesus.cairngorm.command { import com.adobe.cairngorm.commands.Command; import com.adobe.cairngorm.control.CairngormEvent; import com.softwarejesus.cairngorm.event.SuperEvent; import com.softwarejesus.cairngorm.model.GenericModel; import mx.collections.ArrayCollection; import mx.controls.Alert; import mx.rpc.IResponder; import mx.rpc.events.FaultEvent; import mx.rpc.events.ResultEvent; public class SuperCommand implements Command, IResponder { private var object:GerenicModel; public function execute(event:CairngormEvent):void { var newEvent:SuperEvent = event as SuperEvent; this.object = newEvent.object; } public function result(data:Object):void { var event:ResultEvent = data as ResultEvent; object.resultData = event.result as ArrayCollection; } public function fault(info:Object):void { var event:FaultEvent = info as FaultEvent; Alert.show(event.fault.faultString); } } } |
With good middle-tier design, you can eliminate the need to implement 80-90% of your web service result handlers. Thereby saving you valuable time, which you can turn around and dedicate to World of Warcraft or whatever it is you choose to do with your spare time.