Using Dictionary Objects to Map Classes and Instances
20th March, 2009 – 12:05 amThis is a neat little ActionScript 3 trick which I’ve been using more and more of recently that I thought I would share. The basic premise is that it allows you to use a dictionary which maps Classes to other data, the twist is that you can supply either the Class definition or an instance of the Class as the key.
The most obvious example I have come across for this is when creating Asset Mappings. Let’s say I have a generic Model-esq class, ie: a Car and I know that each instance of Car (Ferrari, Porsche, etc) has an assosiated asset (ie: a SWF file). These assets are stored on the server and use the name property as part of that assets filename, for example:
- Ferrari maps to /flash/assets/cars/ferrari.swf
- Porsche maps to /flash/assets/cars/porsche.swf
Now, it’s clear that the “/flash/assets/cars/” part of the URL is common to all instances of the Car class and we can leverage this fact when building URLs with the help of a URLManager Class and a common interface that all Model objects which contain Asset Paths share:
Car Class
/**
* Example Model Class for a Car
*/
public class Car implements IContainsAssetPath
{
/**
* The name of this Car.
*/
private var name : String;
/**
* Constructor.
* @param name The name of this car.
*/
public function Car(name : String)
{
this.name = name;
}
/**
* Returns the name of this car.
*/
public function getName() : String
{
return this.name;
}
/**
* Return the filename for this Car’s Asset SWF.
*/
public function getAssetPath() : String
{
return this.name + ".swf";
}
}
}
IContainsAssetPath interface
/**
* Common Interface for all Models with Assets
*/
public interface IContainsAssetPath
{
/**
* Returns the final stub path to this asset.
*/
function getAssetPath() : String;
}
}
Finally, the URLManager which will help build the URLs for all Models which implement IContainAssetPath
URLManager Class
{
import flash.utils.Dictionary;
/**
* Helper for building URLs from Car Objects.
*/
public class URLManager {
/**
* Dictionary which will contain mappings between Class Definitions
* and URL Stubs used to build asset paths.
*/
private static var objectMappings : Dictionary = new Dictionary();
/**
* Singleton Instance.
*/
private static var instance : URLManager;
/**
* Singleton Accessor Method.
*/
public static function getInstance() : URLManager
{
if (instance == null)
{
instance = new URLManager();
}
return instance;
}
/**
* Private Constructor for Singleton Pattern.
*/
public function URLManager()
{
createObjectMappings();
}
/**
* Populate the ObjectMappings Dictionary.
*/
private function createObjectMappings() : void
{
// Here we create relationships between the Class definitions and their
// URL Stubs. We use the constructor object to create a common relation
// between ClassDefinitions and instances of those Class Definitions.
objectMappings[Car.prototype.constructor] = "/flash/assets/cars/";
}
/**
* Retrieve the AssetURL for the supplied instance of the IContainsAssetPath interface
*/
public function getAssetURL(source : IContainsAssetPath) : String
{
// Here we can extract the constructor object from the incoming IContainsAssetPath
// implementation. This will be the same object as the one we stored in
// this.createObjectMappings();
var sourceConstructor : Object = (source as Object).constructor;
// Check to see if a mapping exists for this Class Definition.
if (objectMappings[sourceConstructor] == undefined)
{
throw new Error("Asset Mapping does not exist for object: " + source);
}
// We can now build the complete URL.
return objectMappings[sourceConstructor] + source.getAssetPath();
}
}
}
The technique relies on the fact that the Class Definition’s prototype will yield the same constructor object as the instance of that Class Definition. This has come in incredibly handy when I only have the Class Definitions avaliable to me whilst writing code but need to create mappings to them at run-time. For those that want a complete the example, here is the source.