« 10 months | Main | Nihilism »

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.

Tags: as3 flex pattern singleton

Comments

Check out "Singleton Take 2" written by Daniel Hai at Ted Patrick's JAM. It's a short and sweet implementation that doesn't require the enforcer.
I agree with you the last version is the cleanest. I had been using the private helper class outside the package. Great post.
Some people might prefer this method if for some reason they don't want it in memory until it's called. :) private static var inst:Singleton; public static function getInstance():Singleton { return inst || (inst = new Singleton()); } public function Singleton():void { if( inst ) throw new Error('singleton error'); }
as we're shaving lines off code - this struck me as neat: public class TestSingleton () { public static var inst : TestSingleton // must be called getInstance or method fails public static function getInstance() : TestSingleton { return inst || inst = new TestSingleton() } } then in the code: import mx.core.Singleton Singleton.registerClass("test",TestSingleton) Singleton.getInstance("test")// and you're done the Singleton class has [ExcludeClass] , so it won't show in code hinting, and therefore presumably it isn't supported in future revisions. Tidy, though.
The code given by Sam Robbins is exactly what i am using often.
private static var inst:Singleton; 

public static function getInstance() : Singleton { 
  return inst || (inst = new Singleton()); 
} 

public function Singleton() : void { 
  if (inst) throw new Error("singleton error"); 
}
But i like the idea from Daniel Hai. Thanx for the advice! :)
For Daniel Hai's example, where would you put the instantiation code? Would you put it in the constructor, which in my head, would never get run? -Dan
The first time that the class is referenced (i.e. some code references Singleton.instance) the VM will load the class and initialize the static instance. At that point the instance variable is still null and the check in the constructor won't get triggered. You can then place your instantiation code in the constructor and know that it will only be called once.
Instead of using a getInstance() function, I prefer to make a public static const instance, and refer to it as Singleton.instance.
Daniel Hais approach is indeed pretty nice. But keep it mind, that this always performs an eager initialization of the singleton, which may not always be, what you want. If you really need lazy initialization your second approach is still the way to go.
Seems like you all forget to include an example showing how to *use* all these singleton implementations (ie: generate a so called singleton and use it). Ok, it may seem obvious to all of you but maybe not to everyone ? Anyone could come up with an example on how to use the latest (and cleanest) method ? Would be very helpfull to me :)
The extra singleton enforcer has always bugged me, so i set out to find a way around it.
This is what i came up with:

package {

	public class DataHandler {
		
		private static var _singletonSerial:Number = -1;
		private static var _instance:DataHandler;
		
		public function DataHandler(singletonSerial:Number = 0) {			

			if (_singletonSerial == -1 || _singletonSerial != singletonSerial) 
				throw new Error("DataHandler is a singleton class, use getInstance() instead.");

          		if (_instance != null)
            			throw new Error("Only one DataHandler instance should be instantiated.");
		}
		
		public static function getInstance():DataHandler {
			
			if (_instance == null) _instance = new DataHandler(_singletonSerial = Math.random());
			return _instance;			
			
		}
		
		
	}
	
}
The Sam Robbins code is incorrect.
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
Good one thank you

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()" );

  }

  //

}

Post a comment

(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)



About