Using a Static Proxy to a Singleton With Events

While I don’t recommend it as standard practice, there have been a few cases where I have found it useful to use a set of static (class level) methods to proxy a singleton implementation. This simplifies access so that you can do this:

MyClass.myMethod(param);

instead of this:

MyClass.getInstance().myMethod(param);

This can be achieved relatively simply, while maintaining the advantages of Singleton, by exposing static methods that pass requests on to instance methods via a standard getInstance call:

class com.gskinner.demos.StaticInterfaceDemo {
// private class members:
private static var instance:StaticInterfaceDemo;
private static function getInstance():StaticInterfaceDemo {
if (instance == undefined) { instance = new StaticInterfaceDemo(); }
return instance;
}
// static "proxy" interface:
public static function myMethod(p_param:Number):Number {
return getInstance().iMyMethod(p_param);
}
// constructor:
private function StaticInterfaceDemo() {
evtDispatcher = {};
EventDispatcher.initialize(evtDispatcher);
}
// instance methods:
// we'll use an "i" prefix to differentiate instance methods
// that are proxied by a static method. Plus, iAnything is cool!
private function iMyMethod(p_param:Number):Number {
return p_param * 2;
}
}

Where this gets a little tricky is if you want the class to act as an event dispatcher. The class needs to expose addEventListener and removeEventListener as static methods, but if you use EventDispatcher.initialize(this) in the constructor, it will try to set up those methods on the instance. This will cause problems, because in Flash, you can’t have class and instance level methods with the same name. Instead, you can set up a generic object to act as an event dispatcher, and redirect all the event logic to it.

Because it should look like the events are being sent from the class, not the generic object, you’ll also want to intercept dispatchEvent calls and set the target. The result looks something like this:

import mx.events.EventDispatcher;
class com.gskinner.demos.StaticInterfaceDemo {
// private class members:
private static var instance:StaticInterfaceDemo;
private static function getInstance():StaticInterfaceDemo {
if (instance == undefined) { instance = new StaticInterfaceDemo(); }
return instance;
}
// static "proxy" interface:
public static function myMethod(p_param:Number):Number {
return getInstance().iMyMethod(p_param);
}
//event dispatcher:
public static function addEventListener(p_type:String,p_obj:Object):Void {
getInstance().evtDispatcher.addEventListener(p_type,p_obj);
}
public static function removeEventListener(p_type:String,p_obj:Object):Void {
getInstance().evtDispatcher.removeEventListener(p_type,p_obj);
}
// instance properties:
private var evtDispatcher:Object;
// constructor:
private function StaticInterfaceDemo() {
evtDispatcher = {};
EventDispatcher.initialize(evtDispatcher);
}
// instance methods:
// we'll use an "i" prefix to differentiate instance methods
// that are proxied by a static method. Plus, iAnything is cool!
private function iMyMethod(p_param:Number):Number {
// just to demonstrate this:
dispatchEvent({type:"testEvent"});
return p_param * 2;
}
private function dispatchEvent(p_evtObj:Object):Void {
// set the target to the class, and then route it through
// the evtDispatcher object:
p_evtObj.target = StaticInterfaceDemo;
evtDispatcher.dispatchEvent(p_evtObj);
}
}

You get the shared data and global access benefits of Singleton, with the ease of use of an instance. It looks like this in use:

import com.gskinner.demos.StaticInterfaceDemo;
StaticInterfaceDemo.addEventListener("testEvent",this);
trace("result: "+StaticInterfaceDemo.myMethod(4));
// traces "result: 8"
function testEvent(p_evtObj:Object):Void {
trace("target is SID? "+(p_evtObj.target == StaticInterfaceDemo));
// traces "target is SID? true"
}

You can download the above code by clicking here.

If memory serves, this will translate even better in AS3, because you can finally instantiate EventDispatcher directly.

Questions?

Grant Skinner

The "g" in gskinner. Also the "skinner".

@gskinner

8 Comments

  1. Very interesting. I like the proxying solution, but I’m not sure I understand why you need the evtDispatcher object. Couldn’t you just do this:

    EventDispatcher.initialize(StaticInterfaceDemo);

    And define the add/remove/dispatch events in the class as empty functions:

    public static funcion addEventListener:Function;

  2. Hi Grant,

    If accessing methods is your concern, how about the following. Since Actionscript is threadless, the following would be perfectly legal:

    private var _instance = MySingleton.getInstance();

    _instance.myMethod( “Foo” );

  3. As much as possible, I am trying to preserve the Singleton implementation whilst wrapping it in a simpler interface.

    Injecting things into a class using Mixins seems really messy to me (and iirc it doesn’t work properly with EventDispatcher for some reason anyway).

    An important part of Singleton is the abstraction of instantiation. I suppose being that this is all within a single class, I could reference the instance directly, but I prefer to have that extra layer and know I could change the instantiation rules by editing a single method.

  4. Hey Grant,

    EventDispatcher doesn’t work when you try to initialize it from a static method because the class is not available yet. You can’t reference ANY CLASS you import from inside of a static method if that method is being called from the class itself (unless there is a way to specify load order ala AS1 #initclip ORDER)

    Your example works because you are calling getInstance on a method call which is being made from the timeline after all classes have loaded / initialized. However, if you changed the static member instance to call getInstance() directly you would lose reference to any class you are importing.

    So while I like how your solution works around the import issue I still prefer not to try to hack a static class because of the added bloat from having to have 1 static method for every instance method. In addition, you lose the benefit of being able to reference the class using a variable and declaring its type (IE: var a:StaticInterfaceDemo = StaticInterfaceDemo; a.myMethod(4);) Granted, you could use getInstance if you made your getInstance method public, but then the code would be a little inconsistent and could just add confusion.

    my 2 cents anyways. =)

    -erik

  5. Erik,

    Yes, that was the problem – order of initialization.

    I agree, this method adds a little bit to file size, and has a small amount of overhead associated with redirecting the method calls. This is why I said I don’t recommend it as a standard practice, but it can be handy at times.

    In the few cases where I’ve found this to be useful, it’s for low access classes with slim APIs, so the overhead is minimal, and being able to reference it as a typed instance is a non-issue.

  6. Hi Grant,

    I was recently introduced to by the book “object oriented action script for flash 8 “ to a great way to use the “__resolve” function in a decorator implementation, I was wondering if there is a way to use “__resolve” function to get a direct access to singleton function as well without the need to declare a new object?

    Here is the decorator script:

    class Decorator {

    private var decoratorInstance:Object;

    function Decorator(decorateObj:Object) {

    decoratorInstance = decorateObj;

    }

    public function __resolve(methodName:String) {

    if (decoratorInstance[methodName]) {

    return function() {

    return decoratorInstance[methodName].apply(decoratorInstance, arguments);

    }

    } else {

    trace(methodName+” method dose not exist!”);

    }

    }

    }

  7. when would you use a private static class?

  8. when would you use asp.net page caching instead of a session caching mechanism?

    .

Leave a Reply

Your email address will not be published. Required fields are marked *