Wed 28 Nov 2007
Proper private constructors for ActionScript 3.0
Posted by bernie under Client-side web programming[7] Comments
(*note to photography subscribers: this is a photography and programming blog, if you only want to read articles on photography, subscribe to the photography-only feed)
While I prepare the next big article, here’s a useful tip for Flash and Flex programmers. ActionScript 3.0 is great, but has no Singletons.
Samuel Agesilas has a solution that successfully prevents subclasses from calling the constructor using super(), but it doesn’t prevent other code from creating new instances of a class with new
Therefore I present to you a crafty hack to make a constructor truly private: add a required argument to the constructor, which must be set to a secret value that is only known to the class:
class Singleton {
private static var _instance:Singleton = null;
// secret known only to this class
private static const secret:Number = Math.random();
/**
* @private
*/
public function Singleton(enforcer:Number) {
if (enforcer != secret) {
throw new Error("This class can't be instantiated directly");
}
}
/**
* Global single instance
*/
public static function get instance():Singleton {
if (_instance == null) {
_instance = new Singleton(secret);
}
return _instance;
}
}
This method also has the advantage that it is very fast, and offers a level of compiler protection: new Singleton() will fail at compile time. A user could make the compilation work by passing in, for example new Singleton(42), but there is a very high* chance of that failing at runtime.
Use this technique whenever a constructor should only be called from within a class, to save your users from the all too easy mistake of of creating a second instance of a singleton.
* Geeky aside: the change of guessing the result of Math.random() is 1 to 2^52 against, or 1/4,503,599,627,370,496. This is because 64 bit floating point numbers have a 52 bit mantissa.
Update: Grant Skinner has another way of doing the same thing. His is probably more elegant, but less amusing, and amusingness is a trait I value in language hacks. Also, his solution is not checked at compile time. Andrew Trice had a go too. His version requires an extra argument so provides compile time checking, but can be defeated by calling new Singleton(Singleton.getInstance). Thanks for the links shaun.
Update 2: Ho hum, Eric J Feminella has a solution that is better than mine, since it is properly checked at compile time, i.e. you can’t fool it by passing in any int, and falling back on runtime checking (though null would work). I still contend that my solution is more amusing though :o)
