In the first article in this ad-hoc series I expressed my desire to replace the Cairngorm developers expressed preference for implementing the ModelLocator as a singleton with an alternative implementation as a monostate. My first attempt at this, using Flex’s binding features, didn’t work. So instead, in parts two and three of this series, I pursued the idea of simply hiding the singleton to create a class that feels like a monostate. As with a monostate, that solution creates classes that all have the same state. However, that solution uses duplication to create multiple identical states rather than one single shared state as with a monostate.

Having pursued that pattern to a conclusion, I returned once more to the monostate. The monostate pattern describes a class that can be instantiated as often as one likes and where each instance uses the same set of data – there is only one state shared by all instances, hence the name. The usual implementation is a set of private static variables and a set of public getter/setter methods for accessing them. Something like this.

package model
{
  [Bindable]
  public class UserData
  {
    private static var _username:String;
    public function get username():String
    {
      return UserData._username;
    }
    public function set username( value:String ):void
    {
      UserData._username = value;
    }
  }
}

This does create a working monostate class, but unfortunately the binding doesn’t work. When Flex creates the bindings on the setters and getters, it creates code so that the binding event is triggered when the setter is called. This doesn’t work for our monostate because, when we set the data from one instance, only that instance’s setter is called and so only that instance’s bindings are updated. We want the binding event to trigger on all instances of the class whenever the setter in one instance of the class is called. To get around this, I rejected the [Bindable] meta-data tag in favour of wiring up the bindings by hand.

The result is a base class, a custom event class and any number of data classes for accessing the monostate.

The base class

package model
{
  import flash.events.EventDispatcher;
  import flash.utils.getQualifiedClassName;
  import mx.events.PropertyChangeEvent;
  
  public class ModelData extends EventDispatcher
  {
    private static var _values:Object = new Object();
      private static var _staticEventDispatcher:EventDispatcher
                                            = new EventDispatcher();
      
    private static function _addInstance( obj:ModelData ):void
    {
      _staticEventDispatcher.addEventListener(
                                      ModelUpdateEvent.MODEL_UPDATE,
                                      obj._update, false, 0, true );
    }
    protected static function _setValue( obj:ModelData, 
                                         name:String, value:* ):void
    {
      var fullname:String = obj.className + "_" + name;
      if( _values[ fullname ] !== value )
      {
        var oldValue:* = _values[ fullname ];
        _values[ fullname ] = value;
        _staticEventDispatcher.dispatchEvent( new ModelUpdateEvent(
                                      ModelUpdateEvent.MODEL_UPDATE,
                                      getQualifiedClassName( obj ),
                                      name, oldValue, value ) );
      }
    }
    
    protected static function _getValue( obj:ModelData,
                                                  name:String ):*
    {
      var fullname:String = obj.className + "_" +  name;
      return _values[ fullname ];
    }
    
    public function ModelData()
    {
      ModelData._addInstance( this );
      className = getQualifiedClassName( this );
      className = className.substring( 
                                className.lastIndexOf( "::" ) + 2 );
    }
    
    private function _update( ev:ModelUpdateEvent ):void
    {
      if( getQualifiedClassName( this ) == ev.qualifiedClassName )
      {
        this.dispatchEvent( PropertyChangeEvent.createUpdateEvent(
                    this, ev.property, ev.oldValue, ev.newValue ) );
      }
    }
    
    private var className:String;
  }
}

The event class

The base class uses an event, that I called the ModelUpdateEvent. The event class looks like this.

package model
{
  import flash.events.Event;

  internal class ModelUpdateEvent extends Event
  {
    public static var MODEL_UPDATE:String = "modelUpdate";
    
    public var qualifiedClassName:String
    public var property:String;
    public var oldValue:*;
    public var newValue:*;
    
    public function ModelUpdateEvent(type:String,
                         qualifiedClassName:String, property:String,
                         oldValue:*, newValue:* )
    {
      super( type, false, false );
      this.qualifiedClassName = qualifiedClassName;
      this.property = property;
      this.oldValue = oldValue;
      this.newValue = newValue;
    }
    
    override public function clone():Event
    {
      return new ModelUpdateEvent( type, qualifiedClassName,
                                     property, oldValue, newValue );
    }
  }
}

The data classes

These are the actual model classes. Each data class must extend the ModelData base class. An example, implementing the username property, looks like this

package model
{
  public class UserData extends ModelData
  {
    [Bindable(event="propertyChange")]
    public function get username():String
    {
      return ModelData._getValue( this, "username" );
    }
    public function set username( value:String ):void
    {
      ModelData._setValue( this, "username", value );
    }
  }
}

Now, instances of this model class can be created and bound to, and when the data is updated by any one instance, all the other instances will send an update event to their bound properties.

The base class and the event class never change. You can use one or more data classes for all the properties in the model and for each class, the instances will share the same data and the bindings will work.

This technique also forces one to use getter/setter pairs for the model, rather than simple public properties. There’s an obvious disadvantage to this – it requires more code. However. there are also advantages, particularly the fact that our model is now a set of functions rather than a set of properties, which means we could define an interface that it implements and we can extend the model classes and override some of the getters or setters, and we have more options for testing.