Patterns for Java Events

Joseph Bergin

You are writing a Java program with buttons, fields, or other components and you need to develop the event structure.

Think of the Java Event Listeners as if they were Command objects that are responsible for carrying out the commands on your application when the user manipulates the user interface. The listeners/commands are part of the functionality of your application, not its interface. In other words, they are the Controllers that form the "glue" between the Views and the Model, not part of the Views themselves.

Forces that apply here. We want to avoid non-object-oriented hand dispatching of methods (using switch statements) and let the Java infrastructure handle basic control flow. We want to be able to add additional GUI elements and even complete views to our basic application framework without modifying much existing code. We want the command objects (listeners) that carry out user instructions to the application to be associated with the application framework in a local way--and not part of the View structure.

One Component Per Listener

Create one listener object for each component and add the listener to the component. The methods of the listener class will handle the events for that component only.

For example, you have an interface with two buttons. Then use two different listeners, one for each button. These may come from the same class or different classes. See Factor for Functionality


Button redButton = new Button("RED");
redButton.addActionListener(new ColorActionCommand(Color.red));
Button greenButton = new Button("GREEN");
greenButton.addActionListener(new ColorActionCommand(Color.green));

Using separate listeners means that you should not need to perform boolean tests to determine what should be done in your listener. This has two benefits. First it makes the code run a bit faster. Second it enhances maintainability and extendibility of your code. If you need to add a new component, you don't need to modify existing methods.

Note that the individual listeners do not need to be named. It is enough to create them as arguments to the add listener methods.

Rationale: If you try to have one listener (often the applet or application object itself) act as listener for several components, then you are undoing the basic dispatch that is central to calling methods, and are implementing dispatch by hand. This requires that some sort of switch mechanism be used to distinguish the various components whose events you are handling. The system is better equipped to do this than you are. This switch structure is both error prone and difficult to maintain as the program grows. Leave the dispatching to the system.

Factor for Functionality

Also known as One Class Per Functionality Group

Some of your components need to react in a similar way but you can distinguish between them based on a small number of values or parameters. For example, a Calculator applet may have several buttons, but the numeric buttons all behave in a similar way, except that each must handle a different digit.

Create one class of event listeners for all of the components with similar behavior, but still create one listener object for each of these components. These listeners will all be members of the single listener class. You will need a constructor for this class that accepts the value or values that distinguish each listener from the others.

For example in the above situation, we can create a single ColorButtonListener class:


class ColorActionCommand implements ActionListener
{	public void actionPerformed(ActionEvent e)
	{	-- whatever using myColor
	}

	public ColorActionCommand(Color c)
	{	myColor = c;
	}

	private Color myColor;
}

If the components are not similar in function do not attempt to use the same class to create the listeners. Instead write a new class for each different functionality grouping of the components.

In the case of the calculator, there are also function buttons ("+", "-" ...) in addition to the digit buttons. Use one or more classes for these buttons, distinct from the class that defines the digit button listeners.

The extreme case is one in which each component acts differently from all others. This called One Class Per Listener

Inner Listeners

You need to write one or more listener classes. Where should you put these classes?

All of your listener classes should be inner to the class that contains the component that they listen to. For example if you have created a new Panel object to hold the buttons shown above, then the listener classes should be inner to this panel class. This keeps the definition of the listener local to the place at which it is used. The panel then contains both the buttons and the button listeners.

Since you are applying One Component Per Listener you do not need wider visibility.

This also gives the listener class access to other parts of the enclosing class that may be needed to handle the event. This in turn minimizes the number of parameters needed when the listener object is constructed. It does, however, increasing coupling between the enclosing class and the listener class. If the enclosing class is revised, it may be necessary to revise the inner class as well.

Share Only When Identical Operation is Required and Guaranteed

You have several components and some of them must behave identically to others.

Give the same listener to all components that are guaranteed to respond identically.

Sometimes the logic of the problem is such that two components must necessarily react in exactly the same way. In this situation, the same listener should be used for both components. Then, updating the processing for one component automatically does so for the other. If you have two listeners/commands, then you will need to keep them in sync as the problem changes. This can cause maintenance problems.

For example, sometimes you want a Button to perform some action on the contents of a TextField. If you also want the same action to be performed when the user hits the enter key while the field is active, then the same listener should be used for both the button and the field.

Delegate When Requirements Can Change Individually

You have components that behave identically, but realize that this situation can change in the future.

Sometimes the current design indicates that two components must react identically, but it is realized that this could change in the future.

In this situation, apply One Component Per Listener and One Class Per Listener. Have the action methods of one of these classes (the delegator) simply call those of the other. This requires that the delegator class have a constructor taking a parameter that is the other component's listener. This in turn requires naming that listener.

If the situation changes in the future and the two components require distinct actions then this delegator class will need to be rewritten.

Component Listens to Itself

(This pattern is due to Ed Epp of University of Portland.) You are designing a GUI with many components. How can you make it clear which listener goes with which component.

For each component group of the same kind that must act in the same way build a class that extends the AWT component and implements an appropriate listener interface. Have the constructor add this as the listener.

For example. Suppose you want to use a button (or set of buttons) that behaves in a certain way. Build and use a class like

class FooCommandButton extends Button implements ActionListener
{	public FooCommandButton (...)
	{  ...
 	   addActionListener(this);
	}

	public void actionPerformed(ActionEvent e)
{...
}
}

This class can be placed anywhere that it is convenient. It can be an inner class, or it can be a public or private top level class.

If another component of a different class must act in the same way as this one, such as a field whose action listener should perform identically to a button, add this component as the listener of the other one. That is, a FooButton can be added as the listener for a TextField.

Note that this pattern subsumes all the earlier ones except Inner Listeners. By making these classes inner classes you gain the advantage of visibility of the fields and methods of the containing classes when you write the actionPerformed method. This can be very useful. Therefore it may be best to combine Component Listens to Itself and Inner Listeners.

This pattern has some negative consequences, however. It is only really useful when there is only a single GUI and the component must only field a small set of possible events. However, if there are multiple interfaces to the same underlying application, it can be difficult to share the commands between them, since the commands are within GUI components. If the component must respond to multiple events, such as focus events or component events, for example, then these classes become unwieldy and encapsulate too much in one place that should probably be separated.


Last Updated: May 12, 2000