Concepts / Mouse and Key Events in Java

Mouse and Key Events in Java

Event-oriented programming lets classes communicate by having one class emit events that other classes listen for and respond to.

  • Programming

The Display That Does Not Call Anyone

Mouse clicks, key presses, and action events are the first event types most Java learners meet. By this point you have seen how a single event is handled. This article looks at the same mechanism from a different angle: how events let separate classes cooperate while staying independent of one another.

Imagine a program made of several classes. One class does the heavy work, and other classes need to display or respond to what that work is doing. The natural first guess is that the working class must somehow call methods on the classes that need the information. The Mandelbrot Viewer is built so that this does not happen. Its MandelbrotDisplay class knows almost nothing about the rest of the program. Yet other parts of the program clearly react to what the display is doing.

The question this article answers is how that is possible, and why the design is worth the effort. Keep the question in mind as you read: if the display never calls the panel or the menus, how do they find out what is happening?

Two Ways to Tell Another Class Something

Suppose the MandelbrotDisplay wants the MandelbrotPanel and the Menus objects to know that its size has changed or that a computation has started. The source spells out the most direct option. The display could call methods such as displaySizeChanged() or computationStarted() in the MandelbrotPanel and Menus objects. The display would be telling those classes what is going on by reaching into them directly.

Now consider the second option, which the viewer actually uses. The display does not call anything in the panel or the menus. Instead, it emits events. The panel and the menus have set up listeners to hear those events, and they respond when an event arrives. In this design, the display announces what has happened and leaves the reaction to whoever is listening.

MandelbrotDisplaycalls methodsMandelbrotPaneldisplaySizeChanged()MandelbrotDisplayemits eventsMandelbrotPanellistens and respondsMenuscomputationStarted()Menuslistens and respondsdirect method calldirect method callevent deliveredevent delivered
How does the dependency between two classes differ when one calls the other directly versus when it only announces events that the other listens for?

Read the two columns as two different designs. In the left column, the display must know the names of the classes it talks to and must call their specific methods. In the right column, the display only announces that something happened. The reaction is the listener's responsibility, not the display's.

Emitters, Listeners, and Registration

Definition

Event-oriented programming lets classes communicate by having one class emit events that other classes listen for and respond to.

Two roles appear in every event-based design. The emitter is the class that fires events when something happens. In the Mandelbrot Viewer, the MandelbrotDisplay object is the emitter. It emits events of various types when various things happen. Two examples the source gives are a change in the size of the display and the start or completion of a computation of an image.

The listener is a class that has attached itself to the emitter so that it hears those events. In the viewer, MandelbrotPanel and Menus set up listeners. The status bar in MandelbrotPanel reports the new display size. Certain menus in the Menus class are disabled when a computation begins and re-enabled when it completes. In both cases, the responding class gets its information from the event, not from a method call made by the display.

Listeners must attach before the events they care about are fired. This is registration. A listener registers with the emitter ahead of time, and only then does the emitter's later firing reach it. The timing matters: registration happens during setup, while an event is fired later, when something actually occurs, such as a computation starting. A class that registers after a computation has already completed will not hear that completion.

MandelbrotDisplayemitter of eventsMandelbrotPanellistenerI18n.trthe one call the displaymakesMenuslistenerholds a reference by designlistens for eventslistens for eventscalls
Which classes hold references to which other classes when events are used, and where does the emitter have no reference to its listeners?

The diagram shows the one call the display makes, which is to I18n.tr. The display has no arrow pointing at the panel or the menus. The arrows that point at the display come from the classes that listen to it. The figure is a simplified picture of the flow the source describes in prose, so treat the prose as the authoritative description.

Strong Coupling Versus Weak Coupling

Coupling describes the degree to which classes depend on each other. The source defines two kinds. Strong coupling occurs when one class directly calls methods in another class, making the two highly dependent. Weak coupling occurs when classes communicate through events or other indirect means, which reduces how much they depend on each other.

Strong coupling is a question of what a class must know. If the display calls displaySizeChanged() in the panel, the display must know that the panel exists, what it is called, and what methods it offers. Any change to those names or methods affects the display. Weak coupling asks less of the emitter. The display only needs to know how to announce an event. It does not need to know who receives it, how many receivers there are, or what they do with it.

The source states the consequence directly. Because events are used for communication, MandelbrotDisplay is not strongly coupled to the other classes. It can be used in other programs without any modification and without access to the other classes. Changing or replacing the panel or the menus does not require changing the display, because the display never names them.

Key point

Events produce weak coupling because the emitter announces what happened and never names the classes that react. The emitter can be reused in another program, since that program only needs to listen to its events, not to match a specific set of method calls.

Following One Computation Through the Viewer

Now trace one event from start to finish. The source describes the computation of an image as one of the things the display reports. The sequence below follows that flow. Each arrow is a message, and the order of the arrows matches the order in which things happen.

MandelbrotDisplayemitterMenuslistenerMandelbrotPanellistener1. registered as listener earlier2. registered as listener earlier3. computation started event4. disable chosen menus5. computation completed event6. re-enable chosen menus
What happens, step by step, when a computation begins and ends, and which objects are involved in each step?
Worked example

Tracing the menus during one computation

The Menus class disables certain menus when a computation begins and re-enables them when it completes. Using only the event mechanism, explain which class decides which menus change, and which class merely announces that the computation has begun.

  1. 1
    Identify the emitter

    The MandelbrotDisplay starts the computation and emits an event announcing that a computation has begun. It does not decide anything about menus.

  2. 2
    Identify the listener

    The Menus class has set up a listener to hear that event. When the event arrives, the Menus class is the one that disables the chosen menus.

  3. 3
    Check for a direct call

    The display does not reach into the Menus class to disable anything. The menu change is caused by the listener responding, not by a method call made from the display.

  4. 4
    Trace the completion

    When the computation completes, the display emits a completion event. The Menus listener responds by re-enabling the menus it disabled earlier.

Result

MandelbrotDisplay is the emitter and announces what is happening. Menus is the listener and decides what to do about its own menus. The display never names or controls the menus.

When a Direct Reference Is the Right Choice

Events are not a cure-all, and the source is careful about this. It says that not everything can be done with events, and that not all strong coupling is bad. The reasoning matters more than the rule. Strong coupling is a problem when the dependency does not match the design. When the dependency is part of what the design is for, it causes no harm.

The MandelbrotPanel class refers directly to the MandelbrotDisplay class, and it cannot be used without it. The source explains why this is acceptable. The whole purpose of a MandelbrotPanel is to hold a MandelbrotDisplay, so the dependency matches the design and causes no problem. A panel that displays a display is expected to know about it.

Mistakes When Reading an Event Design

  • Common mistake

    Assuming the emitter must call the listener to make it react

    What it looks like

    Believing the MandelbrotDisplay must call a method on Menus to disable the menus during a computation.

    Why it fails

    In the event design, the display only announces that a computation has begun. The Menus listener responds to that announcement on its own.

    Do this instead

    Look for the event announcement and the listener that hears it, not for a method call from the emitter to the listener.

  • Common mistake

    Treating every dependency between classes as bad

    What it looks like

    Concluding that MandelbrotPanel is poorly designed because it refers directly to MandelbrotDisplay.

    Why it fails

    The source explains that the panel's whole purpose is to hold a display, so the dependency matches the design.

    Do this instead

    Ask whether the dependency is part of what the class is for before deciding that it is a problem.

  • Common mistake

    Confusing registration with firing

    What it looks like

    Thinking a listener hears events that were fired before it registered.

    Why it fails

    A listener only hears events fired after it has attached itself to the emitter.

    Do this instead

    Separate setup, when listeners attach, from runtime, when events are fired.

  • Common mistake

    Believing that weak coupling means the classes have no relationship at all

Spotting Emitters and Listeners in a Description

MEDIUM

A generated scenario: a TemperatureSensor class measures a room's temperature and announces each new reading as an event. A Dashboard class registers to hear those readings and updates a display. A Logger class also registers and writes each reading to a file. The TemperatureSensor never names the Dashboard or the Logger. For this scenario, identify which class is the emitter, which classes are the listeners, and whether the sensor is strongly or weakly coupled to them. Then say whether the sensor could be reused in a different program without changes.

Hints
  • Ask which class announces that something happened and which classes say they want to hear about it.
  • Strong coupling means the sensor would have to call methods on the Dashboard and the Logger by name. Weak coupling means it only announces readings.
  • Consider whether the sensor would need to change if the Logger were removed from the program.

What do you think happens?

If the Logger listener is never registered with the sensor, what happens to the sensor's announcements?

Reveal answer
Answer:

The Logger does not receive them, because a listener only hears events after it has registered with the emitter. The sensor still emits its events, and the Dashboard still responds if it is registered.

This follows from the registration rule: listeners attach to the emitter in advance, and only then does the emitter's firing reach them. The emitter's behavior does not change when a listener is absent.

Key Takeaways

  • Event-oriented programming lets one class emit events that other classes listen for and respond to, instead of calling methods on those classes directly.
  • Strong coupling means one class directly calls methods in another, so the two are highly dependent. Weak coupling means classes communicate indirectly, such as through events.
  • In the Mandelbrot Viewer, MandelbrotDisplay is the emitter, while MandelbrotPanel and Menus are listeners that respond to its events. The display never names them.
  • Because the display does not depend on the panel or menus, it can be reused in other programs without modification.
  • Direct, strongly coupled references are acceptable when the dependency matches the design, such as MandelbrotPanel holding a MandelbrotDisplay.