In the previous post in this series, I showed how we can use a second class to access the ModelLocator singleton. This enables instances of this second class to be created, modified, bound to and disposed of as often as one wishes while maintaining the integrity of the data in the core singleton. Each instance of the second class mirrors the data in the singleton, modifying the data in an instance modifies the singleton, and modifications to the singleton are immediately reflected back out to every instance of the second class.

The point to this is to hide the singleton behind the second class. This second class becomes our access point to the model data. We can create as many instances as we like. We can create an instance and bind to it in a view. We can create an instance and modify its data in a command or delegate. etc. And we overcome many of the problems with singletons. We still have some issues because there’s a singleton in the background, but it is hidden from our main application code. We program to the second, access class, not the singleton.

If you have read the previous post, there’s nothing very new in this one. Just a refinement to make the pattern simpler to work with. First is the singleton, which I will call the Model.

package model
{
 import flash.events.Event;
 import flash.events.EventDispatcher;
 import flash.events.IEventDispatcher;
 import flash.utils.Proxy;
 import flash.utils.flash_proxy;
 import mx.events.PropertyChangeEvent;
 import mx.events.PropertyChangeEventKind;
 use namespace flash_proxy;

 [Bindable("propertyChange")]
 dynamic internal class Model
                      extends Proxy implements IEventDispatcher
 {
  // do the singleton bit
  private static var _instance:Model;
  
  public static function getInstance() : Model
  {
   if (_instance == null)
   {
    _instance = new Model();
   }
   return _instance;
  }
  
  public function Model()
  {
   if ( _instance != null )
   {
    throw new Error("Attempt to create two instances of the Model.");
   }
   _data = {};
   _eventDispatcher = new EventDispatcher(this);
  }
  
  // do the dynamic bunding bit
  protected var _data:Object;
  protected var _eventDispatcher:EventDispatcher;
  
  flash_proxy override function getProperty(name:*):*
  {
   return _data[name] || null;
  }
    
  flash_proxy override function setProperty(name:*, value:*):void
  {
   var oldValue:* = _data[name];
   _data[name] = value;
   var kind:String = PropertyChangeEventKind.UPDATE;
   dispatchEvent(new PropertyChangeEvent(
                      PropertyChangeEvent.PROPERTY_CHANGE,
                      false, false, kind, name, oldValue,
                      value, this));
  }
  
  public function hasEventListener(type:String):Boolean
  {
   return _eventDispatcher.hasEventListener(type);
  }
    
  public function willTrigger(type:String):Boolean
  {
   return _eventDispatcher.willTrigger(type);
  }
    
  public function addEventListener(type:String, listener:Function,
                      useCapture:Boolean=false, priority:int=0.0,
                      useWeakReference:Boolean=false):void
  {
   _eventDispatcher.addEventListener(type, listener, useCapture,
                      priority, useWeakReference);
  }
    
  public function removeEventListener(type:String, listener:Function,
                      useCapture:Boolean=false):void
  {
   _eventDispatcher.removeEventListener(type, listener, useCapture);
  }
    
  public function dispatchEvent(event:Event):Boolean
  {
   return _eventDispatcher.dispatchEvent(event);
  }
 }
}

This class is dynamic. Any properties dynamically created on it are bindable. It’s the same as in the previous post.

Next up is a class that I call the ModelData. This is a base class for the specific Model access classes.

package model
{
 import flash.utils.describeType;
 import mx.binding.utils.BindingUtils;
 
 internal class ModelData
 {
  private var _model:Model;
  
  public function ModelData()
  {
   _model = Model.getInstance();

   var description:XML = describeType( this );
   var className:String = description.@name;
   className = className.substring(className.lastIndexOf("::")+2);
   for each( var property:String in description.accessor.@name )
   {
    BindingUtils.bindProperty(this,property,_model,className+property);
    BindingUtils.bindProperty(_model,className+property,this,property);
   }
  }
 }
}

This class binds its properties to properties in the Model class’s singleton. This base class should be extended to create specific data classes for the model. These data classes are very simple, like this

package model
{
 [Bindable]
 public class UserData extends ModelData
 {
  public var username:String;
  public var firstName:String;
  public var lastName:String;
 }
}

We then modify the model through an instance of the data classes, like this

var localUserData:UserData = new UserData();
localUserData.username = "John Smith";

We create another instance and bind to it in our view, like this

<model:ModelLocator id="model" xmlns:model="model.*"/>
<mx:TextInput text="{model.username}"/>

That’s all. Like I said, nothing very new. That’s for the next post.

In the comments for the previous post, Jason pointed out that since every instance of UserData contains a copy of the data from the Model, we will use more memory this way, and in some cases this may be a lot more memory which could cause problems. This is a legitimate concern and is one of the issues that makes this solution less than perfect. However, it may not be as bad as it first appears.

  • Many instances of these data classes are briefly created, used then destroyed – either as local variables within methods or as properties within commands (Cairngorm commands are created when needed and disposed of immediately after).
  • Complex objects will be passed around by reference, so the data classes will contain references to the same objects as used in the Model. The actual object isn’t duplicated, just the reference.
  • It is sensible to have a number of different data classes for accessing different parts of the model. Doing this means that each instance of a data class only duplicates a portion of the data in the Model, which further minimises the duplication.

The solution described above is not ideal. But, what I like about it is

  • The model classes one creates are very simple (just a list of public properties, or private properties with public getter/setters if one wishes)
  • Creating and binding to these classes couldn’t be simpler – there’s no need to know about singletons to use this method.
  • The data classes could easily be replaced with classes using test data or other forms of mock-objects for testing.

Whether this is really better than simply using a singleton is open to debate. Personally, I simply find the exploration interesting. I’m still not committed to this solution and next time I’ll look at another solution that holds promise – a working version of the Monostate that I outlined in the first article in this (improvised) series.