Singleton Pattern in AS3
AS3 does not support private or protected constructors which makes it harder to implement the singleton pattern. Below are some approaches I've run across on the Internet, problems with them, and what I hope (please tell me if I'm wrong) corrections to get a real singleton pattern working.
First up is an entry by Andrew Trice about Singletons in AS3. His code was:
// faulty example package { public class Singleton { private static var singleton : Singleton; public static function getInstance() : Singleton { if ( singleton == null ) singleton = new Singleton( arguments.callee ); return singleton; } //NOTE: AS3 does not allow for private or protected constructors public function Singleton( caller : Function = null ) { if( caller != Singleton.getInstance ) throw new Error ("Singleton is a singleton class, use getInstance() instead"); if ( Singleton.singleton != null ) throw new Error( "Only one Singleton instance should be instantiated" ); //put instantiation code here } } }
You can defeat this approach with:
var a:Singleton = new Singleton(Singleton.getInstance); var b:Singleton = new Singleton(Singleton.getInstance); // a !== b
The constructor is doing a function reference comparison, but the function being compared to is available to the caller which is why it can be passed in to defeat the test.
I was also pointed at an approach created by Matt Chotin and posted to Flex Coders. This is a direct cut'n'paste so there are some syntax errors.
// faulty example package whatever { public class MySingleton { public function MySingleton(singletonEnforcer:MySingletonEnforcer) { }private static var instance:MySingleton;
pubic function getInstance():MySingleton {
if (instance == null)
instance = new MySingleton(new MySingletonEnforcer());
return instance;
}
}
}//this is in MySingleton.as but is outside the package block
class MySingletonEnforcer {}
You can defeat this approach with:
var a:MySingleton = new MySingleton(null); var b:MySingleton = new MySingleton(null); // a !== b
If you don't know about private classes I wrote up some information. This is just a missing null check in the constructor to make sure that a valid reference was passed in. I like this approach better overall since it has more compile time support. Trying to call "new MySingleton()" gives an "expected 1 argument" compile time error and trying to call "new MySingleton(XXX)" with anything but null will give you a class cast exception. But that is my personal preference.
I'd also recommend that you add final to the class definition. While I'm pretty sure you can't get access to stuff that easily in AS3, it is probably best to guard against subclassing.
If you want to use the first approach, it can be fixed with the introduction of a private method:
package { public final class Singleton { private static var singleton : Singleton; public static function getInstance() : Singleton { if ( singleton == null ) singleton = new Singleton( hidden ); return singleton; } private static function hidden():void {} //NOTE: AS3 does not allow for private or protected constructors public function Singleton( caller : Function = null ) { if( caller != hidden ) throw new Error ("Singleton is a singleton class, use getInstance() instead"); if ( Singleton.singleton != null ) throw new Error( "Only one Singleton instance should be instantiated" ); //put instantiation code here } } }
The second approach can be fixed with the introduction of a null check:
package whatever { public final class MySingleton { public function MySingleton(singletonEnforcer:MySingletonEnforcer) { if (singletonEnforcer == null) { throw new Error ("MySingleton is a singleton class, use getInstance() instead"); } }private static var instance:MySingleton;
public static function getInstance():MySingleton {
if (instance == null)
instance = new MySingleton(new MySingletonEnforcer());
return instance;
}
}
}//this is in MySingleton.as but is outside the package block
class MySingletonEnforcer {}
This final example, which I think is best, comes from Daniel Hai via Ted Patrick's JAM. The entry is "Singleton Take 2":
package {
public final class Singleton {
private static var instance:Singleton = new Singleton();public function Singleton() {
if( instance ) throw new Error( "Singleton and can only be accessed through Singleton.getInstance()" );
}
public static function getInstance():Singleton {
return instance;
}
}
}
I've updated Wikipedia with this last example. Please change it if you notice any problems.
Comments
Posted by: Josh | October 2, 2006 9:53 PM
Posted by: Sam Robbins | October 23, 2006 10:11 PM
Posted by: Daniel Hai | November 9, 2006 9:27 PM
Posted by: Lz | March 21, 2007 4:18 PM
Posted by: Remo | June 28, 2008 7:22 AM
Posted by: Daniel Roberts | July 16, 2008 4:29 PM
Posted by: DanielR | July 17, 2008 8:22 AM
Posted by: Kai | August 21, 2008 3:50 PM
Posted by: Carsten Schlipf | September 5, 2008 11:06 AM
Posted by: warpdesign | January 7, 2009 1:22 PM
Posted by: Mark | February 7, 2009 5:33 AM
It allows for any number instances to be constructed.
Imagine 3 calls to the constructor in a row.
inst has never been set, errors are never thrown, and multiple instances are constructed.
Lz it is a shame that you are using that code often! it's incorrect
Posted by: SFoster | April 9, 2009 5:11 PM
Posted by: Deva | August 4, 2009 2:42 PM
Another problem is with inheritance. Flex don't let you call super after a throw, so you have to check first if its possible to create the instance and throw the Error on the else clause. Like
public function Singleton() {
if( !instance ) {
super();
} else {
throw new Error( "Singleton and can only be accessed through Singleton.getInstance()" );
}
//
}
Posted by: lxbrito | March 10, 2010 2:19 PM
Posted by: abed allateef qaisi | December 27, 2010 4:41 AM