AS3: Singleton design pattern
It has been a while since I was thinking how to bypass AS3 incapability of private constructors. I was looking for a suitable solution, but none of the found has met my expectations - to be simple, solid and reusable. So I decided to summarize these approaches and bring a new universal one. Let's crack it!
Theory
If you don't know what the expression "singleton" means, let's walk through a bit of a theory. In object-oriented paradigm exist object templates called classes. When a class is instantiated, an object with its signature is created. There is no theoretical limit in count of these created objects and every one of them is unique and has its unique identifier in computer memory.
In certain applications though we would like to have only one instance of given class at a time. We will not discuss the problem of accessing this instance across the program now. Let's focus on the singleton class specifics:
- Need of having one and the same instance for a whole lifetime of the application (e.g. DI containers, contexts).
- Allocation of high amount of computing performance or memory per instance (e.g.. AI controllers).
- Connection with another unique application resource (e.g. database).
- Object recycling & reusability
So there are basically two ways how to achieve single-instance solution for these cases in the application:
- Application somehow controls instances count and returns proper instance on demand and creates a new one if it doesn't exist.
- Leave this responsibility to the class itself.
Second way is the most used way and represents the principle Singleton design pattern has been evolved from. It is a creational pattern and is also used as a base for other design patterns. Its implementation is strongly based on programming language (i.e. C++ allows private constructors), but there is a few common characteristics:
- Class contains static field of its own type.
- Class is constructed by static method.
- Class constructor has private visibility (if supported).
All these points are quite straightforward. Class has to encapsulate its own instance because as we said, it is its responsibility to implement the principle. In that case class cannot be created by its default constructor as it always returns a new instance and if language supports a feature that constructor is private. So the only way to operate the class is with static method. Quite simple, don't you think? :)
Singleton vs. static class
Let's clear this one first. If we want to use some unique functionality, it occurs to every programmer he could use static class solution instead of singleton and as bonus save some resources. Although Singleton pattern uses static fields and methods, it never can be a static class! The differences between static class and singleton-based solution are:
- Singleton can implement interfaces.
- Singleton can be derived or inherited.
- Singleton is stored on heap, static objects in stack.
I hope it is clear now how to determine whether use static classes or singletons.
Solution
So, the solution is based on idea that multiple instantiation is protected by the class itself. In modern programming languages there is a possibility of using a private declared constructor. The only way how to instantiate such class is from inside and logically the only access to non-instantiated classes is via static methods. ActionScript lacks private constructors functionality, so we have to add some more logic to prevent the constructor's default behavior. Let's see the example:
public class Singleton { private static const UNLOCKED:uint = 0; private static const ACTIVE:uint = 1; private static const LOCKED:uint = 2; /** * Lock */ private static var instanceLock:uint = UNLOCKED; /** * Instance holder */ private static var instance:Singleton; /** * @throws Error */ public static function getInstance():Singleton { if (instanceLock == UNLOCKED) { instanceLock = ACTIVE; instance = new Singleton(); instanceLock = LOCKED; } return instance; } /** * @throws Error */ public function Singleton() { if (instanceLock == ACTIVE) { try { // YOUR INSTANTIATION CODE } catch (Error) { // if instantiation failed, keep the class usable instanceLock = UNLOCKED; } } else { throw new Error("Singleton ERROR: " + instanceLock == UNLOCKED ? "Using class constructor is not allowed!" : "Multiple instances of " + this + " are not allowed!"); } } }
You can see three constants defining class states. In UNLOCKED state class is not instantiated, but is ready to be. The only way how to to do it is by calling getInstance() method. If default constructor is used, an exception is thrown. The ACTIVE state defines atomic transition allowing the class constructor to be called. If the instantiation is successful, class state changes to LOCKED, the created instance is stored in instance field and is returned from now with every attempt to re-instantiate the class.
Kind of academic solution? Yes, it is quite universal for non-parametric constructor, has all error states treated and is quite self-explanatory. Let's simplify it!
Minimalistic solution for non-parametric constructors
We can of course get rid of the state constants and use the instance field as the state holder. You can ask what does the line instance = this do.
public class Singleton { /** * Instance holder */ private static var instance:Singleton = new Singleton(); public static function getInstance():Singleton { return instance; } public function Singleton() { if (instance) { throw new Error("Singleton error!"); } // YOUR INSTANTIATION CODE instance = this; } }
Final solution
The previous solution doesn't need the transition state, but it also can't be expanded to a parametric. So we can adjust it a little bit for use with constructor parameters:
public class Singleton { /** * Instance holder */ private static var instance: Singleton; public static function getInstance(...params): Singleton { return instance || (instance = new Singleton(params)); } public function Singleton(...params): void { if (instance) { throw new Error("Singleton error"); } // YOUR INSTANTIATION CODE instance = this; } }
This solution is minimalistic, simple and sufficient for most cases. Its biggest flaw is that it allows direct constructor call. You can of course find many other implementations all over the internet, some of them use detection of a callee during before instantiation, others use an extra constructor parameter to detect proper instantiation attempt. I think good programmer doesn't need these totally bullet-proof and ugly solutions.
Let's hope there will be private constructors in ActionScript 4, if it ever comes to the light :) I hope you enjoyed this article!
Comments
Please feel free to leave a comment related to the topic. Posts violating the netiquette will be deleted.
comments powered by Disqus