Event Generator Idiom

When and How to Make a Java Class Observable

by Bill Venners
August 15, 1998

First published in JavaWorld, August 1998
Summary
In this installment of the Design Techniques column, I propose the "event generator" as a Java idiom. The article provides a background on the concepts of patterns and idioms, describes the observer pattern, and demonstrates the idiomatic way to implement the observer pattern in Java.

In January, I began a long series of articles about designing classes and objects. In this month's Design Techniques article, I'll continue that series but with a different approach. Up to now I have been writing in terms of design guidelines. In this article I'll be writing about idioms.

In my view, some design ideas are most effectively communicated via a set of guidelines, whereas other ideas can be communicated best as patterns or idioms. One of my main goals with this column is to "try out" material that I plan to put into my next book, Flexible Java (see Resources), by sending the material out to the public arena and getting feedback. Currently, I plan to organize Flexible Java around both guidelines and idioms, using each where it is most appropriate. In this and the next article, I'll be proposing some idioms on which I welcome any and all feedback.

I call the idiom I will propose in this article the event generator. This idiom arises from implementing the well-known observer pattern in Java by applying the JavaBeans/1.1 AWT/Swing event model to classes that aren't necessarily beans or GUI components. In this article, I'll present a backgrounder on patterns and idioms, discuss the observer pattern, demonstrate the event generator idiom, and explain why I don't think using the Observer/Observable types from java.util is the best approach to implementing the observer pattern in Java.

On patterns and idioms
In this article, I will be demonstrating the idiomatic way to implement the observer pattern in Java. The observer pattern is one of 23 design patterns described in the book Design Patterns: Elements of Reusable Object-Oriented Software by Gamma, Helm, Johnson, and Vlissides (aka the Gang of Four). This book describes patterns that pop up again and again in object-oriented designs, independent of implementation language. It discusses the pros and cons of each pattern, gives advice on when each pattern is appropriate, and shows an example of each pattern implemented in Smalltalk or C++.

The examples I include in this article are not simply Java translations of the C++ code given in the Gang of Four book, because Java has an idiomatic way to implement this pattern. Which brings me to idioms.

What is the difference between a design pattern and an idiom? The difference is one of scope. In general, both design patterns and idioms describe solutions to recurring design problems. But with a design pattern, both the problem and solution are generic enough to be independent of implementation language. An idiom, by contrast, is a low-level pattern that is specific to a programming language. For example, a design pattern might describe a way to ensure there is only one instance of a class (the singleton pattern). An idiom might describe a way to return multiple values in Java, given that the Java language doesn't have a built-in multivalued return capability. (See Resources for a link to my proposed idiom that addresses this problem.)

The observer pattern
Many of the patterns described in the Gang of Four book show up in the design of the Java API, including the observer pattern. In fact, the observer pattern appears twice in the Java API: once in the Observer/Observer types defined in java.util, and again as the JavaBeans/1.1 AWT/Swing event model.

Because the event model described here is used by JavaBeans, the AWT (1.1 and beyond), and Swing, I will make the claim that this is the idiomatic way to implement the observer pattern in the Java language. At the end of this article, I'll explain why I don't feel the Observer/Observable types from java.util make the grade.

Idiom Presentation
If you are familiar with the Gang of Four book, or any other book of patterns or idioms, you will recognize in the coming text the customary style of presenting patterns. Typically, pattern descriptions all adhere to a common template (that is, they use a common set of text subheadings).

For example, the template I will use for all the idioms I describe in this column has the following form:

Idiom name

  • Intent
  • Also known as
  • Example
  • Context
  • Problem
  • Solution
  • Structure
  • Example code
  • Implementation guidelines
  • Variants
  • Known uses

The template used to describe patterns varies from book to book, but usually remains the same for all the patterns presented in one book (or in this case, in one column). The template shown above is one I concocted for my Flexible Java project, and I welcome any suggestions you may have as to how it can be improved.

The event generator idiom

Intent
Enable interested objects (listeners) to be notified of a state change or other events experienced by an "event generator."

Also known as
Observer, Dependents, Publisher-Subscriber

Example
One recent afternoon, I was sitting in my makeshift office at home, trying to think of a good example for explaining Java's event model in a Java class I was teaching. I was having trouble thinking of a decent example, when the phone rang. I got up, walked over to the phone, answered it, and had a short conversation. After I hung up, I realized I had my example.

What if, I asked myself, I had to design a software system that modeled a phone and all the objects that might be interested in knowing it was ringing? Certainly people in vicinity of the phone (i.e.,in the same room or house) might be interested in knowing it was ringing. In addition, an answering machine might want to know, as would a fax machine and a computer. Even a secret listening device may want to know, so it could surreptitiously monitor conversations.

I realized the interested parties might change as my program executed. For example, people might enter and leave the room containing the phone. Answering machines, computers, or top-secret listening devices might be attached to and detached from the phone as the program executed. In addition, new devices might be invented and added to future versions of the program.

So what's a good approach to designing this system? Answer: Make the telephone an event generator.

Context
One or more objects (recipients) need to use information or be notified of state changes or events provided by another object (the information provider).

The problem
In Java, one object (the information provider) customarily sends information to another object (the recipient) by invoking a method on the recipient. But to invoke a method on the recipient, the information provider must have a reference to the recipient object. Furthermore, the type of that reference must be some class or interface that declares or inherits the method to invoke. In a very basic approach, the provider holds a reference to the recipient in a variable whose type is the recipient's class.

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:

  • One or more recipient objects must be notified of state changes or events provided by an information provider object
  • The number and type of recipient objects may be unknown at compile-time, and can vary throughout the course of execution
  • The recipient and information provider objects should be loosely coupled

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

  • Define a separate event category class for each major category of events that may be experienced and propagated by the event generator.
  • Make each event category class extend java.util.EventObject.
  • Design each event category class so that it encapsulates the information that needs to be propagated from the observable to the listeners for that category of events.
  • Give the event category class a name that ends in Event, such as TelephoneEvent.

Step 2. Define listener interfaces

  • For each event category, define a listener interface that extends 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.
  • Name the listener interface by substituting Listener for Event in the event category class name. For example, the listener interface for TelephoneEvent would be TelephoneListener.
  • Give the methods of the listener interface verb-based names describing in past tense the situation that triggered the event propagation. For example, a listener method for receiving a TelephoneEvent that was triggered by the phone ringing would be named telephoneRang().
  • Each method should return 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)

  • For each listener interface that contains more than one method, define an adapter class that implements the interface fully with methods that do nothing.
  • Name the adapter class by replacing Listener in the listener interface name with Adapter. For example, the adapter class for TelephoneListener would be TelephoneAdapter.

Step 4. Define the observable class

  • For each category of events that will be propagated by instances of this class, define a pair of listener add/remove methods.
  • Name the add method 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().
  • For each method of each event listener interface, define a private event propagator method that takes no parameters and returns void in the event generator's class that fires (propagates) the event.
  • Name the event propagator method 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().
  • Update the code of the event generator's class so that it invokes the appropriate event propagator methods at the appropriate times.

Step 5. Define listener objects

  • To be a listener for a certain category of events, an object's class must simply implement the listener interface for that category of events.

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:

// In file eventgui/ex1/AnsweringMachine.java
public class AnsweringMachine
    implements TelephoneListener {

    public void telephoneRang(TelephoneEvent e) {
        System.out.println("AM hears the phone ringing.");
    }

    public void telephoneAnswered(TelephoneEvent e) {
        System.out.println("AM sees that the phone was answered.");
    }
}

// In file eventgui/ex1/Person.java
public class Person {

    public void listenToPhone(Telephone t) {

        t.addTelephoneListener(
            new TelephoneAdapter() {
                public void telephoneRang(TelephoneEvent e) {
                    System.out.println("I'll get it!");
                }
            }
        );
    }
}

Note that AnsweringMachine implements the TelephoneListener interface directly. By contrast, the Person object instantiates an anonymous inner class that subclasses the TelephoneAdapter class. This anonymous inner class overrides the only method of interest to the Person object: telephoneRang().

The last class we need to define is an example application that will exercise all these classes and interfaces:

// In file eventgui/ex1/Example1.java
public class Example1 {

    public static void main(String[] args) {

        Telephone ph = new Telephone();
        Person bob = new Person();
        AnsweringMachine am = new AnsweringMachine();

        ph.addTelephoneListener(am);
        bob.listenToPhone(ph);

        ph.ringPhone();
        ph.answerPhone();
    }
}

When executed, the Example1 application prints out:

The answering machine hears the phone ringing.
I'll get it!
The answering machine sees that the phone was answered.

Implementation guidelines
With the guidelines I list in this section, I am trying to define a default way to implement this idiom. I say default because, unless you have a specific reason to take a different implementation approach, you should automatically use the approach recommended in these guidelines. My theory is that if you adhere closely to the default implementation approach, it will be easier for your fellow programmers to recognize the idiom in your work. More importantly, I feel that such idiom recognition will make it easier for your fellow programmers to understand, use, and change your code.

On the other hand, you should feel free to depart from the default approach to implementing the idiom when you feel it makes sense. In fact, I myself describe two potential "variants" to the default approach in the next section.

Now, on to the guidelines:

  1. Create just one event object for each "firing" and pass it to all listeners.
  2. Make the event object immutable, so there is no possibility that a listener will change the event object as it propagates.
  3. Use a single thread to notify all listeners. In other words, the fire method should go through the list of listeners and invoke the appropriate handler method upon one listener after the other.
  4. Take a snapshot (clone the list) of registered listeners at the beginning of the firing process, then send the event to each listener registered at the time the snapshot was taken.
  5. Keep event handlers (listener methods) short. These methods should execute quickly because (in the default approach) listeners are notified one at a time. In other words, listeners must wait until all listeners before them in the queue have been notified, so it is good citizenship for listeners to be quick about handling events. If an event handler method really needs to do considerable work as a result of an event notification, consider designing the handler such that it fires off or notifies another thread that does the actual time-consuming work.
  6. Don't write listeners such that they depend on an order of notification.
  7. If a listener is not interested in all events that compose a particular event category, consider subclassing an adapter class and just overriding the methods of interest.

Variants
The following are variants to the default approach to implementing idioms:

  1. If there are vast differences in the frequency of events in a particular event category, consider defining separate listeners for high frequency and low frequency events. An example of this approach is illustrated by the MouseListener and MouseMotionListener interfaces of java.awt.event. Both of these listener interfaces define handler methods for MouseEvents. But because MouseEvents like "mouse moved" are generated so much more often than MouseEvents like "mouse pressed," the high frequency events like "mouse moved" get their own listener, MouseMotionListener. Lower frequency events like "mouse pressed" are handled by methods declared in plain old MouseListener.
  2. If your listeners need to cooperate with each other, consider making the event object mutable so that listeners can communicate to each other through the event object. An example of this variant is seen in the mutable AWTEvent class of java.awt, which is the superclass of all the AWT event classes defined in java.awt.event. Class AWTEvent includes two methods named consume() and isConsumed(), which enable listeners of AWT events to cooperate with one another. A listener can "consume" an event by invoking consume() on the event object. Subsequent listeners can determine that the event has already been consumed by invoking isConsumed() on the event object. If isConsumed() returns true (in other words, if another listener has already invoked consume() on the same event object), the listener can ignore the event.

Known uses
This idiom is based on the delegation-event model used by JavaBeans, the post-1.1 AWT, and Swing.

On Observer/Observable
As mentioned earlier in this article, the observer pattern shows up twice in the design of the Java API: once as the idiom described in this article (for JavaBeans, post-1.1 AWT, and Swing) and once in the Observer and Observable types of java.util. So, why don't I think the Observer/Observable types set a good example for a Java observer idiom?

It turns out that Observer/Observable classes more closely resemble the example code given in the Design Patterns book than they do the event generator idiom described in this article. In my opinion, however, these classes don't make the grade for the following reasons:

  1. Observable is a class you need to subclass to make an object observable. Thus, you have to find a way to fit Observable as a superclass in your observable class's single-inheritance hierarchy. This is often difficult.
  2. To turn a class into an observer (called a listener elsewhere in this article), you need only implement a single interface, Observer. The single method declared in the Observer interface, update(Observable, Object), is used to notify the observers. The Observer interface and update() method are generic so they can be used in just about any situation. Unfortunately, this generic design means that a programmer won't be able to easily understand code that uses Observer/Observable without digging into the nuts and bolts of the update handler methods. Contrast this with a listener that subclasses a MouseAdapter and overrides the mouseReleased() method. You already know a lot about the nature and source of the event just by looking at the names of the superclasses and methods, because they are more specific.
  3. One final reason I turned away from Observer/Observable is simply that using the event delegation model used in JavaBeans in non-bean classes eases any future transformation of a given class into a JavaBean. (Note that the AWT and Swing components, which use this event delegation model, are themselves JavaBeans.)

About JavaBeans
If you are at all familiar with JavaBeans, as you read this article you may have exclaimed, "Hey, this is all just JavaBeans stuff!" If you're thinking it would be better to just make every class a JavaBean, you would by definition use the "idiomatic" style in implementing the observer pattern to propagate JavaBeans events.

For those of you unfamiliar with JavaBeans, the minimum requirements for making a class a bean are simply that the class have a no-arg constructor and implement java.io.Serializable. Although a lengthy treatment of the question of whether or not to make a class a bean is beyond the scope of this article, I include a link to a transcript of an e-mail debate on just this topic in the Resources section. (The resource is titled "To Bean or Not To Bean.") Very briefly, my own opinion on this matter, quoted from the e-mail debate:

If someone is going to use a class in a bean builder, that class had better be a bean. Otherwise, you needn't force it into a bean, though it may be bean-ready by its very nature. I do, however, think you should use the bean/Java naming conventions and JDK1.1 event model scheme regardless of whether your class has a no-arg constructor or implements Serializable.

For the full discussion of the proper time and place to make classes into beans (and a broader array of opinions) check out the "To Bean or Not To Bean" e-mail debate.

In the telephone example above, class Telephone is not a JavaBean, because it doesn't implement java.io.Serializable. I would venture to say that you probably should have Telephone implement Serializable, unless you have a specific reason for not doing so. That way, if client programmers ever want to serialize an instance of the class, their lives will be made easier. In this case, while Telephone isn't a JavaBean, its design benefits from the JavaBeans event delegation model.

Conclusion
In my world view, the two main benefits of idioms, such as the event generator idiom described in this article, are:

  1. Idioms, like patterns, establish a vocabulary for discussing design and serve as an effective way for less experienced programmers to benefit from the hard knocks of more experienced programmers.
  2. Code that uses idioms is easier to understand, use, and change (for those programmers familiar with the idioms).

The event generator idiom allows one or more listener objects to be notified of state changes or events provided by an event generator. The number and type of listeners may be unknown at compile-time, and can vary throughout the course of execution. The loose coupling of listeners and event generators make the code easier to change or reuse in changing situations.

Next month
In next month's Design Techniques, I'll continue the series of articles that focus on designing classes and objects. Next month's article will describe several basic object idioms.

A request for reader participation
I encourage your comments, criticisms, suggestions, flames -- all kinds of feedback -- about the material presented in this column. If you disagree with something, or have something to add, please let me know.

You can either participate in a discussion forum devoted to this material or e-mail me directly at bv@artima.com.

Resources

This article was first published under the name The Event Generator Idiom in JavaWorld, a division of Web Publishing, Inc., August 1998.

Talk back!

Have an opinion? Be the first to post a comment about this article.

About the author

Bill Venners has been writing software professionally for 12 years. Based in Silicon Valley, he provides software consulting and training services under the name Artima Software Company. Over the years he has developed software for the consumer electronics, education, semiconductor, and life insurance industries. He has programmed in many languages on many platforms: assembly language on various microprocessors, C on Unix, C++ on Windows, Java on the Web. He is author of the book: Inside the Java Virtual Machine, published by McGraw-Hill.