Wed 28 Nov 2007
(*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)
November 28th, 2007 at 7:36 pm
nice one! i’ve seen quite a couple of solutions to this problem, but i like the creativity of yours! surely the enforcer should be a Number though - casting the result of Math.random() to an int will always give you 0, rendering the enforcer useless ;)
November 28th, 2007 at 7:43 pm
Oops! good spot, fixed now. I’ve been going all int happy since AS3 came out, I forget that floating point numbers exist sometimes.
November 28th, 2007 at 9:58 pm
haha! i know what you mean!
November 29th, 2007 at 6:05 pm
hey, just stumbled across this post:
http://www.darronschall.com/weblog/archives/000274.cfm
an interesting read, with links to other interesting reads :)
November 29th, 2007 at 6:14 pm
Awesome, that Eric Feminella version is exactly what I needed; it is better than my solution anyway, if not as good as real private constructors.
November 29th, 2007 at 7:13 pm
The lack of actual Language constructs is a frustrating limitation of ActionScript 3, however, the limitations are helpful in that they force us to dig down a little deeper into the language to find what we can use to develop creative solutions to these limitations. It’s a fun challenge.
I also wrote a post awhile back which explains a way to create Abstracts in AS3 which you guys might find helpful as well.
http://www.ericfeminella.com/blog/2007/01/16/pseudo-abstract-classes-in-as3/
November 29th, 2007 at 7:20 pm
True. Old school JavaScript was a great language for hacking language features, from automagical memoization to AOP, if you understood it well enough. This just carries on in the same tradition.