Let's review some of the fundamental changes that have taken place in the current alpha of Cairngorm 2.0, both from an internal point of view (how we've reimplemented some of the API under the covers) and some of the public API changes we've been compelled to make. We'll then talk about some of the future changes we're thinking of making, and soliciting feedback on.
Change to the ModelLocator API
The implementation of Data Binding has changed significantly in Flex 2.0; in terms of migrating code that leverages data-binding, it's now necessary for us to explicitly mark classes, methods or attributes to which we wish to data-bind, using the [Bindable] metadata. Additionally, it's no longer possible to bind to static attributes in a class - which was the "simplest thing that could possibly work" that we'd elected in our implementation of our ModelLocator pattern we introduced to Cairngorm 0.99.
The ModelLocator is therefore now implemented as a true singleton; we provide a getInstance() method on the ModelLocator, and then hold public attributes for our model on that instance. By marking the ModelLocator class as [Bindable] we can now bind to the ModelLocator using this notation:
<mx:Text text="{ ModelLocator.getInstance().statusMessage }" />
This is actually back towards the implementation we almost released for Cairngorm 0.99; furthermore, this should be a simple search-and-replace migration once you've udpated your ModelLocator implementation. We provide an example ModelLocator implementation in org/nevis/cairngorm/samples/login/model/ModelLocator.as
Value Objects
Since Value Objects are often used in bindings, we're advocating the following best-practice of always marking your value objects as [Bindable] as follows:
package org.nevis.cairngorm.samples.login.vo
{
import org.nevis.cairngorm.vo.ValueObject;
[Bindable]
public class LoginVO implements ValueObject
{
public var username : String;
public var password : String;
public var loginDate : Date;
}
}
You'll also note that we implement the ValueObject marker interface; some developers like marker interfaces, some don't, and we've provided one and will leave you all to make your own decisions about your own preferred best-practice.
EventDispatching leverages the Flash Player 8 Event Model
In Cairngorm 0.99, we differentiated Cairngorm Events (events that are the result,typically, of a user-gesture) by broadcasting them via our own EventDispatcher class. Your typical dispatch of a Cairngorm event was to use code in the form EventDispatcher.getInstance().broadcastEvent( ... ).
Under the covers, Flash Player 8 now handles events natively - and we were circumventing all this goodness by using our own EventBroadcasting mechanism. With an eye on performance, and to simplify understanding, we've decided to embrace the underlying Flash Player event model, and to deprecate the use of the Cairngorm EventBroadcaster class to instead use the built-in dispatchEvent() notation. In fact, in the alpha build we give you, we've taken out the EventBroadcaster altogether - to be decided is whether we put that back in, but clearly mark it as deprecated. Your thoughts welcomed.
However, we're keen to differentiate CairngormEvents - you'll find a new CairngormEvent class in the org.nevis.cairngorm.control package. A CairngormEvent is a base-event class that we advocate developers extend, to create application-specific Cairngorm events, such as LoginEvent, LogoutEvent, etc. CairngormEvent will bubble it's events (ensuring that they reach the controller) and does not allow events to be cancelled - if you sneak under the covers of CairngormEvent you'll see how we use the default properties of AS3.0 method parameters to set these for you.
Again, take a look at the sample application to see how we use CairngormEvents; class LoginEvent extends CairngormEvent, and holds a single property - a value object with the user's login details. The login view (Login.mxml) creates and dispatches this event into the system as follows:
public function loginUser()
{
var loginVO : LoginVO = new LoginVO();
loginVO.username = username.text;
loginVO.password = password.text;
var event : LoginEvent = new LoginEvent( loginVO );
dispatchEvent( event );
}
Now, there's no need for you to interrogate event.data.* when you want to handle Cairngorm events, as you'll see in LoginCommand:
var loginEvent : LoginEvent = LoginEvent( event ); delegate.login( loginEvent.loginVO );
Instead, you can fetch your data directly from the event attribute, eg loginEvent.loginVO. If you really want to be lazy about this, and dynamically attach data to a data property, we've used the new * type in ActionScript 3.0 to add the following to a default CairngormEvent:
/** * The data property can be used to hold information to be passed with the event * in cases where the developer does not want to extend the CairngormEvent class. * However, it is recommended that specific classes are created for each type * of event to be dispatched. */ public var data : *;
So if you want to skirt our advocated best-practice, we've given you the hooks to do so; in seriousness though, this should hopefully aid with your migration.
So that's the other key migration to Cairngorm 2.0 for you .. migrate your event broadcasting to use dispatchEvent() and CairngormEvent subclasses where possible.
Change Method Signature on Commands
To accomodate the new CairngormEvents, we've changed the signature of the execute() of the Command interface to take a CairngormEvent; this reflects underlying changes in the FrontController implementation, which you needn't be concerned about to just use Cairngorm.
What lies ahead ?
This is an alpha, so first and foremost, we release this for community feedback. There are a significant number of large Flex applications built upon Cairngorm, and your experience migrating them to Cairngorm 2.0 is of interest to us. Let us know how you get on with the build we've released, and you can steer our next build.
In Cairngorm 0.95, for historical reasons we have to scratch the insides of our heads to remember, Commands are stateful. We don't like this, we've had to work around it on projects of our own, BUT, we have a responsibility to not make changes to Cairngorm that could impact upon all your developments. So if you're relying upon the statefulness as a side-effect, then be warned that in a future Cairngorm 2.0 alpha we're going to make Commands truly stateless - as they should be - so that they don't persist data between invocations. If you require state to be maintained between executions, you'll have to manage that explicitly - as you should have been doing anyway !
We're hugely excited about enterprise data services, and have some thoughts as to how they fit within a Cairngorm architecture; we're still coalescing our thoughts on this internally, and will present our thoughts in a future Cairngorm 2.0 alpha release. If there's anything else that you'd like to see in Cairngorm 2.0, by all means let us know. Our aim is as ever to keep Cairngorm lightweight, to keep it generic, and to not throw into it all the things that others wish were in the Flex product (so we won't add Internationalisation !). That we have reduced the number of classes in Cairngorm 2.0 from Cairngorm 0.99 is proof that we are moving forward in our aim, not moving away from it.
"In sculpture, perfection is attained not when there is nothing left to add, but when there is nothing left to take away"
We look forward to your thoughts.
