Working in Flex, I have been exploring the different ways in which the event structure is used. Form my perspective, the basic event mechanisms are those of the observer pattern. The object that wants to act on the event subscribes to the event and the event is fired by the object that owns the event. However, in the Flex environment event usage has follows a number of different patterns. Three of these are in use in the Flex application that I am working on and in order to understand their best usage, we need to understand the mechanisms that they use and the type of pattern that they implement.
In general event mechanisms are used as a way of notifying that something has occurred in the system. The basic event structure is common to many languages. However, the basic event structure isn't always the best solution to the problem.
The issues that arise when considering event mechanisms are:
- who is interested in acting upon the event?
- who will manage the event?
- who or what will cause the event?
- how many objects will want to respond to the event?
- can the event handler respond to the same type of event from many different dispatchers?
- how many object will dispatch the event?
- is the event monitoring of the event related to the dispatcher of the event or is the dispatcher irrelevant to the dispatch?
- what data is to be passed in the event?
I am sure that there are other issues that influence the decision as to the type of event mechanism to implement.
The basic event mechanism
The basic event mechanism is associated with a specific owning and dispatching object. This wanting to respond to the event register their interest through some form of subscription mechanism such as add and event listener to the expected dispatching object. When the event is dispatched all the listeners are called with the data passed in the dispatch.
With this approach, the dispatching object has no knowledge about the listening objects. It allows any object to register a method to process the event and passes across a data packet that allows the listener to determine what caused the event.
An event listener is not restricted to handling an event from just one object nor is it restricted to listening for just one type of event. However, the handler becomes more complex as the number of different types of events that it processes increases.
One the dispatch side, an particular event is associated with a particular object although other objects may fire the same type of event.
This mechanism is used extensively in Flex to handle user gestures and to notify of changes to objects through the data binding mechanisms. Data binding monitors the changes in the object that it is interested in and updates the object to which it is attached. Many objects can be bound to the same underlying property or object. This provides a one to many form of notification.
Detaching the event
There are cases where the sender of the event isn't that relevant or possibly more correctly, the owner of the event isn't relevant. In the Flex frameworks, the Cairngorm event mechanism appears to be detached from any object other than the command controller. Pure MVC provides another alternative in the form of a notifier. By detaching the event from a specific object, it is possible to implement a broadcast pattern. Any object can fire the event, and any object that is interested in that event can respond. This is the basis of the PureMVC notifier mechanism.
The broadcast mechanism is appropriate where the listener acts on the data passed in the event and is not concerned about who originated the event. In effect it can be a many to many type notification. In this situation, the registration of interest in the event isn't with the possible initiators but rather with some generic event manger that registers the subscriptions and receives the notifications that cause the event to occur.
Restricting the handlers
The other alternative is to restrict the number of objects that can handle the event. This is the way that the Cairngorm events are implemented. A Cairngorm event is designed to cause a specific command to be executed. As a result, it isn't possible to register interest or to listen for the event. All object can do is cause the event to occur and pass in the data that the command processor might expect. The dispatcher of the event doesn't now the object that is registered to handle the event nor whether an object is registered to handle the event. It simply dispatches the event.
Events are attached to specific command objects through a command controller. This is like a method call where the caller has no knowledge of who they are calling but they know that the listener is there and will perform the required service.
This structure implements a many to one form of processing and the Cairngorm framework implements this for its command processing structure.
Which is best?
That is really the wrong question. Each of the mechanisms have there appropriate use.
If the object that needs to receive an event knows the originator then the basic event mechanism resolves the problem. It can record its interest and respond to each occurrence of the event. This is the data binding situation. A field in a visual component want to reflect the value of some property of an object in the model. The model doesn't want to have to manage all the objects that might need updated so it dispatches a property change event when the value changes. All objects bound to that property are notified and can act on the change as required.
If the object interested in receiving an event doesn't know who will cause the event then the notifier or broadcast mechanism works well. The receiving object simply registers its interest in the event and not a specific object / event combination. The objects causing the event have no interest in who will process the event and simply ask the event manager to fire the event thus leaving them selves free of any knowledge of who will process the event.
If an event is to cause a specific processing action and the object that causes the event has no knowledge of which object will process the event then the Cairngorm event mechanism is appropriate. However, in the Flex case, where the code is single threaded, the Cairngorm event behaves like a disconnected method call. The object that handles the event is called immediately unless the processing is delayed by a remote object call or specifically by the call later mechanism.
Problems occur when the mechanisms are used to implement a style of processing that they were never intended to handle. Of course this applies to any design or elementary implementation pattern.