Typesafe Enums in ActionScript 2
8th July, 2008 – 10:24 amTypesafe Enums are perfect when you want to get the message across loud and clear, with no room for error. The classic use for an Enum is handling events, to give some context I’ll demonstrate how you could handle events without using an Enum:
* This example method will send an event back to a Registered Listener, in this
* case we’re going to inform the listener that we have loaded our data and
* are ready for use.
*
* @param Void
* @return Void
*/
private function __onLoadComplete():Void
{
// This forms the basis of the event we are going to send.
var eventObject:Object = new Object();
// Populate our eventObject with its type & the event message to send
eventObject.type = ‘ModelEvent’;
eventObject.event = ‘data_loaded’
// And send the object via mx.events.EventDispatcher
this.dispatchEvent(eventObject);
}
The listener object (in this case, a Controller), will then have a similar method for handling callbacks, again, without using an enum we would be left to switch on a string, something like this:
* Example function, showing us registering the event listener to the Model with
* a delegated callback to the __onModelEvent() function.
*
* @param Void
* @return Void
*/
private function registerModel()
{
model.addEventListener("modelEvent", Delegate.create(this, __onModelEvent));
}
/**
* Callback method which was registered at the same time as adding the eventListener
* this method handles incoming events from registered models
*
* @param Object event object dispatched from registered model.
* @return Void
*/
private function __onModelEvent(event:Object):Void
{
switch (event.event)
{
case ‘data_loaded’:
trace (‘Controller::__onModelEvent — Data Loaded!’);
break;
default:
trace (‘Controller::__onModelEvent — Unkown Model State recieved: ‘ + event.event);
break;
}
}
Obviously this approach works, but it can lead to some quite obvious, yet tricky to find errors. For example, what if I set the value of eventObject.state to ‘Data_Loaded’, or mashed the keyboard and entered ‘data_laoded’? Of course, both these problems can be spotted (the trace() on default will help you debug typos, and a `event.state.toLowerCase()` would help with the first problem, but what about when other people are working with the code - how will they be able to tell which states are available to them, this is especially true if there are multiple listeners to modelEvents, maybe all the state’s aren’t show in each switch() - now we’re starting to see the bigger issue - one of software design. Wouldn’t it be great if we could bring all the Model State messages into one place?
Say hello to the ModelEvent Enum Class:
* ModelEvent.as
* John Reeves, http://www.jonnyreeves.co.uk/
* May 2008
*
* Typesafe Event dispatched from Models.
*/
class ModelEvent
{
private var _name:String;
private var _level:Number;
/**
* Private Constructor for creating ModelEvent instances. This stops developers
* creating their own ModelEvent’s in the code, instead we encourage people
* to create Static Model Events below:
*/
private function ModelEvent(name:String, level:Number)
{
this._name = name;
this._level = level;
}
// Static Events Models can Send.
static public var DATA_LOADED:ModelEvent = new ModelEvent(‘DATA_LOADED’, 1);
static public var DATA_ERROR:ModelEvent = new ModelEvent(‘DATA_ERROR’, 2);
/**
* Returns the ModelEvent object for the given ModelEvent String
*
* @param mediaType the MediaType’s name
* @return the MediaType object
*/
public static function forName(ModelEvent:String):ModelEvent
{
if ((ModelEvent[ModelEvent.toUpperCase()] instanceof ModelEvent)) {
return ModelEvent[ModelEvent.toUpperCase()];
}
}
/**
* Returns the current ModelEvent’s name as a String
*/
public function getName():String
{
return this._name;
}
/**
* Returns the current ModelEvent’s Level as a Number
*/
public function getLevel():Number
{
return this._level;
}
/**
* Compares an incoming ModelEvent (x) to the current Object.
*
* @param ModelEvent x Incoming ModelEvent Object to perform comparison on.
* @return Boolean true if it matches
*/
public function equals(x:ModelEvent):Boolean
{
if (x.getName() == this.getName() && x.getLevel() == this.getLevel()) {
return true;
}
return false;
}
}
This class is used a little differently to most others. As the constructor is set to private, we don’t expect developers to be creating instances of ModelEvent’s in their own code (so in the case of the Model’s __onLoadComplete() function which was shown above, we wouldn’t other developers using the new keyword to create their own instance of ModelEvent). Instead, we expect developers to access the pre-defined ModelStates via the static public properties defined inside the class, let’s show a quick example and revisit our Model’s onLoadComplete method:
* Now we will update this method to use the ModelEvent Enum class.
*
* @param Void
* @return Void
*/
private function __onLoadComplete():Void
{
// This forms the basis of the event we are going to send.
var eventObject:Object = new Object();
// Populate our eventObject with its type & the state message to send,
// this time instead of setting state as a String, we set it to be on the
// ModelEvent’s static properties.
eventObject.type = ‘ModelEvent’;
eventObject.event = ModelEvent.DATA_LOADED;
// And send the object via mx.events.EventDispatcher
this.dispatchEvent(eventObject);
}
That was straight forward - an now we don’t have to worry about typo’s or mixing the case, any mistakes at this point will be caught by the Compiler when you build the swf. Okay, that’s cool, but how do we handle recieving the events in the Controller? Even easier!
* Callback method, this time update to handle the incoming ModelEvent
*
* @param Object event object dispatched from registered model.
* @return Void
*/
private function __onModelEvent(event:Object):Void
{
// extract the ModelEvent Enum from the event Object
var event:ModelEvent = event.event;
// Use the ModelEvent::equals method to perform comparison
if (event.equals(ModelEvent.DATA_LOADED)) {
trace (‘Controller::__onModelEvent — Data Loaded!’);
}
}
Of course, if you prefer you could switch based on event.getLevel() with the case statements performing comparisons on ModelEvent.DATA_LOADED.getLevel();
2 Responses to “Typesafe Enums in ActionScript 2”
How about an update on the new job Reeves?
By Guy Cocker on Sep 25, 2008