Home Contact

[July 20, 2005]

Decoupling Again: Event Mugger, and getInstance() Considered Ugly

Filed under: ActionScript — @ 1:49 am

Having read several times that I ought to be using EventDispatcher, I decided to study it up and get with the program. The advantage, as I understood it, was that objects (or "guys" as they are technically known) in a running program can broadcast messages (or event notifications), and other guys can listen for those events and do whatever they need to do when the event occurs, and the broadcasters and listeners don’t have to have their hooks into each other. So I was rather surprised to find that in order for a guy to receive event notifications he has to sign up for them with the broadcaster, thus requiring that he have a reference to the broadcaster. That’s not what I’d call decoupling.

An obvious solution is to route all the events through a single guy who will broadcast them. Everyone knows where to find him, and no one needs to know who initiated an event just to be able to receive it. (Of course the initiator can identify himself in the data sent as part of the event so a listener can find him once he’s received it.) This calls for the Singleton pattern.

Now in C++ a constructor returns the new instance of the class, so you can implement Singleton by always returning the same guy from the constructor. Users of the class call new Thing; and no one is the wiser. But in ActionScript a constructor doesn’t have a return value, so you don’t have the chance to monkey with it that way. So the Singleton pattern is ordinarily implemented in ActionScript by a class that keeps its constructor private and provides a getInstance() method so others can get hold of the single instance of the class. Well, this works just fine, but it’s half in and half out of the closet. You’re not hiding your singular nature from anybody, Thing; you’re making them keep saying Thing.getInstance().doThis(); and Thing.getInstance().doThat(); So don’t be surprised if they start asking Thing.getInstance().howComeYoureStillSingle(). We can simplify the interface a bit by giving the class some static methods that have the same names as the instance methods and that each invoke the corresponding method on the single instance.

So here’s my EventMgr class:

import mx.events.EventDispatcher;

class EventMgr
{
static var version:String = "$Id: EventMgr.as,v 1.4 2005/07/16 01:11:46 ashaw Exp $";

/*
 * Function:	dispatchEvent
 * @param	eventObject - any object; must have a String property whose name is "type"
 *			and whose value is the name of the event;
 *			may have other properties holding data about the event, such as
 *			the event's originator
 */
public static function dispatchEvent(eventObject:Object)
{ EventMgr.getInstance().dispatchEvent.apply(EventMgr.getInstance(), arguments); }

/*
 * Function:	addEventListener
 * @param	event - name of the event
 * @param	listener - object or function
 */
public static function addEventListener(event:String, listener)
{ EventMgr.getInstance().addEventListener.apply(EventMgr.getInstance(), arguments); }

/*
 * Function:	renmoveEventListener
 * @param	event - name of the event
 * @param	listener - object or function
 */
public static function removeEventListener(event:String, listener)
{ EventMgr.getInstance().removeEventListener.apply(EventMgr.getInstance(), arguments); }

/****************************
 * End of public interface! *
 ***************************/

private function EventMgr()
{
	EventDispatcher.initialize(this);
}

private function dispatchEvent() {};
private function addEventListener() {};
private function removeEventListener() {};

private static var _instance:EventMgr = null;

private static function getInstance():EventMgr
{
	if (EventMgr._instance == null)
	{
		EventMgr._instance = new EventMgr();
	}
	return EventMgr._instance;
}

}

Having written the above, I found that iteration::two reached a virtually identical solution in the Flex context, documented at Separating ActionScript from MXML: Approach 6. They didn’t go the extra step of hiding getInstance(), and they modified the API a bit with a broadcastEvent() method that explicitly requires an eventName:String argument.

iteration::two’s EventBroadcaster implementation is just one part of the Cairngorm architecture for Flash and Flex RIA development: Architecting Flash Applications with Cairngorm. I suspect that as I progress with Flash I may arrive at more solutions that converge with those of other OO developers who’ve gone before. I’ve already seen this happen recently at FlashBelt in Minneapolis (really good conference by the way), where I recognized my own approach to using interfaces and composition in Joey Lott’s talk on design patterns in Flash. So I may as well speed things up by digging into Cairngorm. I’m not developing RIAs (yet!) but I’m still coming up against some of the same architectural issues, even in a photo gallery website. Yes, I’ve heard about not over-architecting things, and if I had to get this out in a week I’d just make it work without making my code quite so general — after all, I did my first Flash project (earlier this year: Sandi Fellman Photography) in AS1, where, having read Branden Hall and Samuel Wan’s OOP with ActionScript, and knowing I’d be switching to AS2 soon, I never attempted doing proper inheritance with all that __proto__ stuff. I’m not even sure who’s doing what in that application, what with all the global functions and all kinds of methods tacked onto MovieClip, but it works very well, and it’s even some kind of debuggable. I just don’t want to develop that way as a rule.

And it is my feeling that proper OO discipline benefits everyone — all the "guys" in the application, as well as myself as programmer, debugger, and customizer, and the client and putative users.

A great introduction to EventDispatcher usage and implementation: Phil Chung’s blog.

An interesting discussion of concerns about memory leaks when using Delegates, especially in the context of a singleton class like EventMgr: Delegates, be careful….


Contents copyright © Alan Shaw 2005-2008

25 queries. 0.218 seconds. Powered by WordPress version 2.5.1