2004-08-31

Implementing Java listeners

The Observer pattern lets you ensure that when an object changes state, all interested objects are notified automatically. In Java, observers are called listeners, and are widely used in the JDK. They have details that are not widely understood, though.

First, a quick reminder: an object you can listen to – called a Subject in "Design Patterns", but given no explicit name and implementing no specific interface in Java – will have public methods to addXListener and removeXListener. The Subject's implementation will have a listenerList, or many listener lists if it supports different kinds of listeners (and chooses to keep them separately, which is uncommon). The Subject's implementation will also have fire methods corresponding to the one or more methods in the XListener interface.

You knew all this.

Why are there so many listeners on a component's list?
Many components have behavior that is implemented using listeners, so you can expect to see more listeners than just the ones your application added. (The reason for implementing a component like this is the belief that if you can use the same interface internally that you offer externally, you should: it makes your external interface better-tested, helps you spot any design flaws in the external interface early on, and reduces duplication.)

What order should listeners be notified in?
In reverse order of addition. (In the common case where you append each new listener to a list, this means that you should iterate backwards over that list.) The reason for this is that it's the only way for client code to modify behavior you implemented using listeners. You can get away with notifying listeners in the wrong order for a long time, but the first person who tries to override existing behavior will be really annoyed when they can't.

How does a listener say "I've handled this; no more listeners should pay attention"?
By invoking consume on the event you were passed. See Apple Technical QA1363: Unsolicited About Boxes for an example of what might happen if you don't invoke a consume method (the method in question isn't actually called consume, which is another indication that this isn't a well traveled path).

So if I invoke consume, no other listeners are notified?
Incorrect. The rest of the listeners will still be notified. Every listener needs to check isConsumed on the event. Usually, you shouldn't respond to a consumed event. You may never have seen a listener that actually makes this check. Luckily, DefaultCaret contains a good example of listener code that does check. Of particular interest is that the mousePressed code needs to take some action when given a consumed event; it just behaves differently given an unconsumed event.

What this means is that not only do you have to implement your notification code correctly, you have to rely on all of your listeners being implemented correctly. And you can't take the protective short-cut of terminating the iteration over the listeners as soon as one invokes consume on your event, because some listeners might still need the notification.

Is there anything a component author can do to help clients write efficient code?
Offer bulk update methods that translate to a single bulk fire (see AbstractTableModel's fire methods and TableModelEvent for an example). For a hypothetical addElement method, you should have an addElements too, so a client can use your component in a more efficient manner.

Another choice is to offer a getValueIsAdjusting method like ListSelectionEvent. The client can then invoke setValueIsAdjusting (see JList) around its mutation. If a client knows its work is expensive, the information it gets from getValueIsAdjusting lets it hold off until you say that you're done changing for now.

"Design Patterns" touches on this problem (point 3 in the "Implementation" section), but offers an inferior error-prone solution: making clients responsible for invoking a fire method. These choices are similar, in that they require a well-behaved client, but it's significantly easier to write these kinds of well-behaved clients, because the extra burden is only placed on the client in the unusual case. The first suggestion is the better, because a good programmer will look for those methods first.

When a listener is added, should I inform them of the current state?
No. It might seem useful, and it often is, but it's not the Java idiom. Any listener that wants to know the current state when it's added will have to ask. The listener's constructor is often a convenient place to do this. You can also move the addXListener boilerplate inside the listener by making the constructor private and exposing a static method that creates and registers the listener (though this is an uncommon idiom).

What about "Chain of Responsibility"?
Chain of Responsibility, another pattern from "Design Patterns" can be thought of as a degenerate case of Observer, where the assumption is that at most one listener (called a Handler in that pattern) is interested in the notification. Strangely, the "Related Patterns" sections for these two patterns fail to mention the other. A recent JavaWorld article demonstrates why you don't want to use Chain of Responsibility, and fails to give any reason why you'd use this (uncommon) idiom in favor of the more common and better Observer pattern.

The modifications suggested in the article move the Chain of Responsibility pattern even closer to Observable, taking iteration back out of the hands of the individual listeners, but because the iteration is implemented through inheritance, you lose the ability to have listener interfaces: you need abstract classes instead. This is a poor choice in a language with single inheritance, such as Java.