Coming from the concept of the Observer design pattern, EventBroadcaster add a level of filtering of which type of events the listener is listening to.
Moreover, this class is an extension of Macromedia’s EventDispatcher, which serve the same aim, but with less type checking. Those two classes are polymorphic, then you’ll be able to use them alternatively.
The real difference between them consist in typing each element and also not using mixins to add event broadcasting features at runtime to a class. As a good OOP designer, the former is a second nature, while the latter should seems a nightmare (IMHO).
Note : In the pixlib framework, one can use EventBroadcaster as a global source or use either inheritance or composition to customize its behavior. As a matter of example, we’ll document here the compostion way to use it.
In order to make that structure working, we need :
All of these pieces are located in the com.bourre.events package.
This is the class that is responsible for the publication of events.
Two ways of using it :
import com.bourre.events.*; class EventSource extends EventBroadcaster { public function EventSource () { super (); } }
The subclass will be used automatically as the target of the event.
import com.bourre.events.*; class EventSource { private var _event:EventBroadcaster; public function EventSource () { // Give the reference as an argument of the constructor _event = new EventBroadcaster (this); } }
We give a reference to the owner’s class to the composed one in order for the target to be give to the event object.
A listener can be any type of object. Each listener can be of a different type.
The different types that request to be notified must first register themselves to the event source : (here is a basic way to do it)
EventSource.addListener (listener);
Once some listeners has been added to its list, the source will be able to send all them some messages as events. The two sign a virtual contract together. And as soon as the listener declares the appropriate event as a method in its signatures, it will be notified.
That way, when the event will be fired, each class will be able to react in its own scope. The listeners are clearly disconnected from the source they have been registered to.
By the way, there are limitations in such a method : how could you be sure to type correctly the method name ? Some (me) would have created an interface, for example INotifiable, that will define all the event and force the listener to implement it :
import com.bourre.events.*; class EventSource { private var _event:EventBroadcaster; public function EventSource () { // Give the reference as an argument of the constructor _event = new EventBroadcaster (this); } public function addListener (listener:INotifiable):Void { _event.addListener (listener); } }
But this design could be a real pain to use. Each new event will have to be implemented on each listener, eventhough it is not used. This structure has to be lighter.
Here are the different possibilities :
The pixlib framework help a bit in this area. If you give a function reference as the second argument of the addListener method, it’ll be automatically used as the event handler :
import com.bourre.events.*; class EventSource { private var _event:EventBroadcaster; public function EventSource () { // Give the reference as an argument of the constructor _event = new EventBroadcaster (this); } public function addListener ( listener:Object, delegateMethod:Function ):Void { // Note the use of apply to make some sort of overloading _event.addListener.apply (_event, arguments); } }
Then, at the time of registering :
class Application { public static function main ( container:MovieClip):Void { var eventSource:EventSource = new EventSource (); var listener:BasicListener = new BasicListener (); eventSource.addListener (listener, listener.onEvent); } }
Obviously, BasicEvent must declare the onEvent in its method list.
You now are able to bind the listener and its listening method on a single statement. Moreover, the compiler will complain if the method is badly implemented. That make the type checking a little bit stronger.
One of the drawback of using the addListener method is that the listener will potentially be notified for each events the source is able to send. This is not a major issue, but that slows the iteration on all the listener of in the list.
That’s why the W3C has commited specifications to handle a fine grained listener scheme. The DOM now uses it.
In order to listen to only some events, you have to use this method of EventBroadcaster :
EventSource.addEventListener ("eventType", listener);
Only the declared type of event will be send to the corresponding observer. Moreover, the framework support also a feature that permit to type check the latter. It provides a EventType class that wrap that responsability.
import com.bourre.events.*; class ClickEvent extends EventType { // CONSTANTS public static var onClick:ClickEvent = new ClickEvent ("onClick"); public function ClickEvent ( name:String ) { super (name); } }
This way provides constants to bypass error.
import com.bourre.events.*; class EventSource { private var _event:EventBroadcaster; // CONSTANTS public static var onClick:EventType = new EventType ("onClick"); public function EventSource () { // Give the reference as an argument of the constructor _event = new EventBroadcaster (this); } }
Here becomes the preceding listener registering :
EventSource.addEventListener (ClickEvent.onClick, listener);
or
EventSource.addEventListener (EventSource.onClick, listener);
Now that you know that pixlib enable automatic delegate when given as the third argument, you could use the same syntax for the addEventListener :
EventSource.addEventListener( ClickEvent.onClick, listener, listener.onClickHandler );
The onClickHandler will be used in the listener class to receive notifications when the event is broadcasted.
When using the Observer pattern, you’ve got to choose between two alternatives to send data between the source and the listener.
The EventBroadcaster gives us an alternate way. Each eventsource could send a BasicEvent or a subclass of it. That class just gives you methods to access the data, in a more decoupled fashion. You can use the getType and getTarget of BasicEvent to retrieve information that you like.
I, for myself, would extend BasicEvent behavior to add getters and setters on it.
import com.bourre.events.*; class ClickEventObject extends BasicEvent { private var _text:String; public function ClickEventObject ( event:ClickEvent ) { super (event); } public setButtonText ( text:String ):Void { _text = text; } public getButtonText ():String { return _text; } }
That way, it’s fairly easy to use the event object as a middle class to bundle data, some kind of Value Object.
We have seen the differents way to use the EventBroadcaster structure in pixlib. There are also several other methods to discover, but they are quiet self explanatory.
Maybe there will be some lucky stiff that will be running out of there sometimes. Just register here and wait for them to appears.