Sure, we’ve all heard of Singleton, the design pattern that limits the number of instances of a class to one. It’s a feel good pattern because it’s easy to use and make us feel smarter in front of our co-workers, even though some would argue that Singletons are just glorified global variables. But for a long time, I have wondered if it would be possible to use the same idea to limit the number of instances to two or more instead of just one. I called this hypothetical pattern an “N-gleton”, pronounced “en-gull-ton”. (UPDATE: Comments from readers inform me that this is called a “Multiton“) It may not be possible to make a poly-instance singleton, it may not be good programming practices, I’m not even sure I can think of a reason why it might be useful, but today, I tried my hand at inventing it and found something else entirely.
The Hard Limit approach
The first attempt I made at the N-gleton was to simply throw an error when the class had been instantiated too many times.
package {
public class NGletonHardLimit {
protected static var maxInstances:int = 3;
protected static var numberOfInstances:int = 0;
public function NGletonHardLimit() {
if (numberOfInstances >= maxInstances) {
throw new Error ("There can only be " + maxInstances + " instances of this class");
}
numberOfInstances++;
// initialize code
}
}
}
This was shortly followed by a slightly more friendly version of the same idea…
package {
public class NGletonRequestInstance {
protected static var maxInstances:int = 3;
protected static var numberOfInstances:int = 0;
public static function requestInstance():NGletonRequestInstance {
if (numberOfInstances >= maxInstances) {
return null;
} else {
return new NGletonRequestInstance(new Key());
}
}
public function NGletonRequestInstance(key:Key) {
// initialize code
}
}
}
class Key {}
However, I soon realized that this method is quite flawed in its logic. The counter is actually just counting the number of times the requestInstance() method is being called rather than truly controlling the number of instances of the class. It also doesn’t allow you to release an instance and decrement the instance count. Perhaps something like this would work in C where you can use alloc and dealloc but for Flash, it doesn’t really make sense. Besides, It doesn’t really seem useful.
Cycling through instances
Next, I tried looping through the same limited set of instances. Each time you request an instance you get a new one until you’ve reached your limit, then it starts over with the first instance again.
package
{
public class NGletonLoop
{
protected static var maxInstances:int = 3;
protected static var numberOfInstances:int = 0;
protected static var instances:Array = [];
public function NGletonLoop(key:Key) {
// initialize code
}
public static function requestInstance():NGletonLoop {
var n:int = numberOfInstances++ % maxInstances;
if (instances[n] == null) {
instances[n] = new NGletonLoop(new Key());
}
return NGletonLoop(instances[n]);
}
}
}
class Key {}
This approach just felt wrong. In fact, it was totally stupid. There is no way to know what you’re going to get when you request an instance and it seems like it would be very easy to overwrite data without even knowing it. You still have the problem of the instantiation being counted rather than the actual objects. Besides, it’s really totally useless.
It was looking like the whole idea of an N-gleton may not really have a practical application. But in the name of theoretical computer science, I chose to press on and try one more idea I had.
Tracking individual instances
The previous idea of storing instances in an Array seemed to make some kind of sense to me but it was missing individual control over the instances. So I decided to try using an index to track the instances. I soon realized that a Dictionary that uses objects as keys might give a more useful identifier than an integer. The maximumInstances didn’t really make sense any more so I took it out. I seemed to be onto something but it wasn’t really the N-gleton I had imagined, it was something more useful. I called it an IndexedObject.
package {
import flash.utils.Dictionary;
/**
* An example of a class that tracks multiple individual instances by using a dictionary.
*/
public class IndexedObject {
/**
* Stores the actual instances of the class.
*/
protected static var _instances:Dictionary = new Dictionary(false);
/**
* The identifier that the dictionary uses to reference this instance.
*/
public function get id():* { return _id; }
protected function set id (id:*):void { _id = id; }
protected var _id:*;
/**
* Gets an instance of the class. Similar to how a singleton would get the
* instance but uses an identifier token to specify which instance to get.
*/
public static function getInstance(id:*):IndexedObject {
if (_instances[id] == null) {
_instances[id] = new IndexedObject(new Key)
}
var instance:IndexedObject = _instances[id] as IndexedObject;
instance._id = id;
return instance;
}
/**
* Returns all of the created instances as an array.
*/
public static function get allInstances():Array {
var array:Array = [];
var obj:IndexedObject;
for each (obj in _instances) {
array.push(obj);
}
return array;
}
/**
* Removes an item from the list of instances.
* Actually, doesn't quite work because an instance referenced by a variable
* outside of the class could still hold a reference to the deleted instance.
*/
public static function deleteObject(id:*):void {
delete _instances[id];
}
/** Just used for testing */
public var name:String;
/**
* Constructor can't be used by outsided classes.
*/
public function IndexedObject(key:Key) {
// Initialize code
}
}
}
class Key{}
So essentially what’s happening here is the Singleton’s getInstance() method is being replaced with one that allows you to specify a particular instance. Any class could create new instances or access the same instances provided they had the same id. Here’s a little demo. I’m using a name property to show the example.
var thing:IndexedObject = IndexedObject.getInstance(5);
thing.name = "thing 5";
thing = IndexedObject.getInstance("foo");
thing.name = "thing foo";
for each (var obj:IndexedObject in IndexedObject.allInstances) {
trace("IndexedObject.getInstance(",obj.id,") =", IndexedObject.getInstance(obj.id).name);
}
Results in:
IndexedObject.getInstance( foo ) = thing foo
IndexedObject.getInstance( 5 ) = thing 5
Voila. The “indexed object”. I think for me the jury is still out on whether this is a good idea. It seems like it might be kind of a hack since it, like Singleton, is essentially just a complicated implementation of global variables but even more so and without Singleton’s benefits. It also intuitively feels like it could be very useful. Either way, I’m proud of it.
So what do you think? What would you use this for? Is it a hack? Pattern or anti-pattern?
Personally I would never use something like this. If I want to limit the number of instances I would go for a Factory class.
I used that pattern extensively in Flashr, my old AS2 wrapper for the flickr API. e.g. getPhoto in http://my-svn.assembla.com/svn/flashr/trunk/com/kelvinluck/flashr/core/Photo.as
It was very useful in that situation (where you know that a photo with a given ID will always refer to the same object) and meant that I could update the photo from anywhere and know that it would be updated everywhere…
I’d be interested to know if the pattern does have a “real” name though.
The pattern you are thinking of is the Multiton…
http://en.wikipedia.org/wiki/Multiton_pattern
A Singleton isn’t a complicated global variable; you simply use the Singleton pattern when you want to enforce the existence of one and only one instance of an object, and of course an object is data and behaviour encapsulated. Confusing Singletons with globals shows a lack of understanding about what an object is. If there should only ever be one instance of a CommsManager class for example, make it a Singleton. This guards against other developers, who may be maintaining your code in years to come, mistakenly creating two instances. If you are using your Singleton as some kind of glorified global variable then, yes, your object design is probably flawed – but I don’t agree with blaming the pattern – blame the designer!
uhm…. your approach remember me the Factory pattern.
but, i can not imagine when it may be useful..
is there any case study for it?
ciao!
Yeah multiton pattern. For a use case and alternate implementation check out Puremvc Multicore :)
I used this pattern this week to ensure only one JavaScript HTML editor was spawned by a given custom Flex TextArea instance. Thus multiple pop-ups could be spawned but no more than a 1 to 1 ratio with associated components.
It is interesting that you used a Dictionary in lieu of a plain old Object. Does it return the same instance if you try to use two different variables as keys that contain the same String? That’d be a zinger for flexibility if so.
Hey Jon,
It does return the same instance if you use two strings with the same value (because of the way strings are evaluated in AS3) but it does not return the same instance if you use two different objects even if the objects contain the same data. So:
getInstance(“foo”) == getInstance(“foo”)
but
getInstance({name:”foo”}) != getInstance({name:”foo”})
Thx for the 411 I like the way you implemented that suspect it’ll come in handy next time.