Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 3 of 5
In the design context covered by this idiom, however, the basic approach of holding a reference to the recipient doesn't work so well. The requirements of this design context are:
The trouble with the basic approach is that the programmer has to know exactly what objects will be recipients when the information provider class is written. In this design context, however, the actual recipients may not be known until runtime.
The solution
The solution is to implement an event delegation mechanism between the information provider (the event generator) and the recipients (the listeners).
Here's a step-by-step outline of Java's idiomatic solution to this problem:
Step 1. Define event category classes
java.util.EventObject.Event, such as TelephoneEvent.
Step 2. Define listener interfaces
java.util.EventListener and contains a method declaration for each event (of that category) that will trigger an information propagation from the
event generator to its listeners.Listener for Event in the event category class name. For example, the listener interface for TelephoneEvent would be TelephoneListener.TelephoneEvent that was triggered by the phone ringing would be named telephoneRang().void and take one parameter, a reference to an instance of the appropriate event category class. For example, the full signature
of the telephoneRang() method would be:void telephoneRang(TelephoneEvent e);
Step 3. Define adapter classes (optional)
Listener in the listener interface name with Adapter. For example, the adapter class for TelephoneListener would be TelephoneAdapter.
Step 4. Define the observable class
add<listener-interface-name>() and the remove method remove<listener-interface-name> (). For example, the listener add and remove methods for a TelephoneEvent would be named addTelephoneListener() and removeTelephoneListener().void in the event generator's class that fires (propagates) the event.fire<listener-method-name>. For example, the name of the event propagator method for the event propagated via the telephoneRang() method of TelephoneListener would be fireTelephoneRang().Step 5. Define listener objects
Structure
These UML diagrams depict the structure of the telephone example, which is shown in Java code in the next section. For information
about UML, see the Resources section.


Example code
Here's some Java code that illustrates using the event generator idiom to pass information from a Telephone object to interested listeners. The first class to define is the event category class, which will be called TelephoneEvent:
// In file eventgui/ex1/TelephoneEvent.java
public class TelephoneEvent
extends java.util.EventObject {
public TelephoneEvent(Telephone source) {
super(source);
}
}
Note that TelephoneEvent extends java.util.EventObject and takes as the only parameter to its only constructor a reference to the Telephone object that generated this event. The constructor passes this reference to the superclass (java.util.EventObject) constructor. Event handler methods can then invoke getSource() (a method defined in java.util.EventObject) on the event object to find out which telephone generated this event.
Requiring an event source reference to be supplied every time an event object is created enables a single listener to register with multiple sources of the same event category. For example, a secret listening device object could register as a listener for multiple telephones. Upon being notified of a telephone event, it could then query the event object to find out which telephone generated the event.
In addition, allowing the handler method to get a reference to the event source object enables the handler to ask the source for more information by invoking methods on the source. This is called the pull model in the observer-design-pattern literature, because the listener is pulling information out of the event generator after being notified of an event. It contrasts with the push model, in which all the information needed by the listener is encapsulated in the event object itself.
On the subject of encapsulating data, note that this event category class does not encapsulate any data of its own. It is
conceivable, however, that a class like this one could be enhanced to contain data such as the telephone number of the caller,
if it is available, the time of day the event occurred, or other relevant information. Such information would need to be supplied
to or produced by the constructor (or constructors) of the event category class and made available to handlers via get methods.
Given the event category class, the next thing to define is the listener interface:
// In file eventgui/ex1/TelephoneListener.java
public interface TelephoneListener
extends java.util.EventListener {
void telephoneRang(TelephoneEvent e);
void telephoneAnswered(TelephoneEvent e);
}
Note that this interface extends java.util.EventListener, a tagging interface that doesn't contain any members. This interface defines handler methods for the two kinds of events
that fall into the TelephoneEvent category: telephoneRang() and telephoneEvent(). Note that both methods accept one parameter, a reference to a TelephoneEvent object, and return void.
Because TelephoneListener declares more than one event handler method, it is a good idea to define an adapter class:
// In file eventgui/ex1/TelephoneAdapter.java
public class TelephoneAdapter implements TelephoneListener {
public void telephoneRang(TelephoneEvent e) {
}
public void telephoneAnswered(TelephoneEvent e) {
}
}
As described earlier in the article, an adapter class should fully implement the interface with methods that do nothing but return. This enables listeners that are not interested in all the events to subclass the adapter and just override the handler methods of interest.
At long last, it is time to make the Telephone object itself into an event generator:
// In file eventgui/ex1/Telephone.java
import java.util.Vector;
public class Telephone {
private Vector telephoneListeners = new Vector();
public void ringPhone() {
fireTelephoneRang();
}
public void answerPhone() {
fireTelephoneAnswered();
}
public synchronized void addTelephoneListener(TelephoneListener
l) {
if (telephoneListeners.contains(l)) {
return;
}
telephoneListeners.addElement(l);
}
public synchronized void
removeTelephoneListener(TelephoneListener l) {
telephoneListeners.removeElement(l);
}
private void fireTelephoneRang() {
Vector tl;
synchronized (this) {
tl = (Vector) telephoneListeners.clone();
}
int size = tl.size();
if (size == 0) {
return;
}
TelephoneEvent event = new TelephoneEvent(this);
for (int i = 0; i < size; ++i) {
TelephoneListener listener = (TelephoneListener)
tl.elementAt(i);
listener.telephoneRang(event);
}
}
private void fireTelephoneAnswered() {
Vector tl;
synchronized (this) {
tl = (Vector) telephoneListeners.clone();
}
int size = tl.size();
if (size == 0) {
return;
}
TelephoneEvent event = new TelephoneEvent(this);
for (int i = 0; i < size; ++i) {
TelephoneListener listener = (TelephoneListener)
tl.elementAt(i);
listener.telephoneAnswered(event);
}
}
}
This class has addTelephoneListener() and removeTelephoneListener() methods that enable listeners to register and unregister themselves with the Telephone object. These methods make sure the internal list of listeners (stored in a Vector) contains no duplicates -- so that each event is reported to each listener only once. If a listener attempts to register
twice with the same Telephone object, it won't be added to the list the second time. Such an overly enthusiastic listener will still be notified of each
event only once.
The fire methods of class telephone clone the Vector of listeners before propagating the event. In this implementation, when an event is "fired," a snapshot is taken of the current
registered listeners, and all those listeners are notified of the event. This means that a listener may be notified of an
event even after it has unregistered itself from a telephone; that's because the event would have been fired before the listener
unregistered itself.
The four classes defined above -- TelephoneEvent, TelephoneListener, TelephoneAdapter, and Telephone -- fully comprise one implementation of the event generator idiom. To see the idiom in action, however, you must define a
few more classes. Here are two simple listeners for this event generator: