Overriding EventDispatcher and Mixed-in Methods in General
May 12, 2004

MM's EventDispatcher class is one of the pieces of code I use the most, over and over, on a daily basis. It allows the immediate inclusion of a strong event model in any project. It makes it easy to add/remove listeners and dispatch events from any class.

Aside from the arguments as to whether it's better implemented as a mix-in or an implementation of an interface, whether it works better as a decorator, and whether it's better to initialize EventDispatcher on the instance or on the class (prototype), I haven't really thought much about any particular downside until recently...

The situation arose out of a necessity to override EventDispatcher's methods (addEventListener and removeEventListener in this case). I wanted to dispatch key events, so I needed to capture key presses, and dispatch events based on those. However, since it doesn't make a lot of sense to have the dispatcher capture those key press events when there are no listeners registered to it (to dispatch those key events to), I thought I could only have my class capture key press events when there is at least 1 listener registered to it. The issue is that there is no way to get access to the number of listeners registered to an event without digging into properties in EventDispatcher (listeners are stored in an array called ["__q_" + event]).

No problem...I can just override addEventListener and removeEventListener in my class so that the class begins to capture key press events when addEventListener is called, and stops when removeEventListener is called and there are no more listeners registered (still need the class to keep and internal list of listeners to do this...crappy), and then call super.addEventListener() or super.removeEventListener(). Well, it turns out that there is a problem...an obvious one.

If you initialize EventDispatcher (either on the instance or the class), it automatically overrides the addEventListener, removeEventListener, etc. methods that you defined in your class. Unfortunately, that's the nature of the mix-in...it sticks stuff into prototype and overwrites anything with the same name that might already exist in there. However, as I see it, there are at least 2 workarounds for this that allow you to override EventDispatcher's methods:

1) Create a superclass that initializes EventDispatcher on it's prototype using a static initialization block (not sure if that's the right term). You can either do it via a static property like this:
static private var initEventDispatcher = 
mx.events.EventDispatcher.initialize(BaseClass.prototype)
or by calling a static function like this:
static private var initEventDispatcher = staticConstruct();
static private function staticConstruct():Void {
 mx.events.EventDispatcher.initialize(BaseClass.prototype);
}
This works because the static initialization code executes when the CLASS is first initialized. Then, if you inherit from this class, the addEventListener and removeEventListener YOU define in your inheriting class will override EventDispatcher's methods. There is an example of this in mx.controls.TextInput, where it's addEventListener overrides EventDispatcher's. The reason why this works is because MM's component framework initializes EventDispatcher in the same way as I mentioned here. Inside mx.core.ext.UIObjectExtensions, there is a static function called Extensions which initializes EventDispatcher (actually UIEventDispatcher) on UIObject.prototype.

Of course, this method only works if you have a super class you can inherit from.

2) The only other way I could think of doing it (this method does not involve a super class) was to store the methods in a temporary container so they don't get overridden, then reset them after EventDispatcher.initialize is called. Something like this:
public function MyClass() {
 __addEventListener = addEventListener;
 __removeEventListener = removeEventListener;
 EventDispatcher.initialize(this);
 addEventListener = __addEventListener;
 removeEventListener = __removeEventListener;
}

public function addEventListener(evt:String,handler):Void {
 // override EventDispatcher.addEventListener here
 super.addEventListener(evt,handler);
}

public function removeEventListener(evt:String,handler):Void {
 // override EventDispatcher.removeEventListener here
 super.removeEventListener(evt,handler);
}
Not the prettiest way to do it, but it works.

Something to keep in mind if you ever need to override mixed in methods.

Posted by philter at May 12, 2004 07:29 AM

Comments Disabled