How do “modern” libraries handle GUIs? More specifically, how does Swing enable GUI development? Answering these questions was paramount to getting the necessary working knowledge to build my little graph path visualization app (and more).
Here’s the cliff’s notes version: GUIs are typically event-driven. I have a background in Operations Research, where we refer to certain classes of simulations as Discrete Event Simulations. GUIs fit this profile exactly. In a Discrete Event Simulation, time is of secondary importance. We define a sort of event-vocabulary, in which we model the state of the “world” or simulation environment as responding to a sequence of changes. When a change happens, the simulation applies an appropriate chunk of code to update the state of the world, possibly scheduling more changes to happen in the future. This goes on and on and on until there are no pending changes, or we tell the simulation to stop.
When you go with an event-driven view of the world, you’re basically compressing time, and only responding to things that matter (events) to make changes. Since you’re not concerned with things that don’t cause changes (events), you don’t waste time constantly checking to see if changes have occurred. The alternate view, of constantly checking to see if anything changed, is known as polling, where we check the state of the world at fixed intervals to determine how to update the state.
Why are modern GUIs event-driven? Well, it’s much more efficient. The operating system is technically polling the hardware (things like mouse clicks, keyboard presses, etc.) very very quickly, and is capable of providing a ton of redundant information for the GUI to handle. We don’t want to deal with the extra overhead of polling redundant information (are you going to click the mouse again in 1x10\^-6 seconds? probably not….) . Additionally, if we increase the time between polling, we might miss events as they occur. This leads to weird behavior and a lack of “real-time” feeling for the GUI.
On the other hand…polling the state of the world makes things relatively simpler. We can define centralized processing logic to check for and react-to changes in the world state, and then just bang on the state every 10ms. In some cases, this is “good enough”, and actually desirable since it’s probably simpler to implement. Going the event-driven route (especially with mutable state involved, and side-effecting operations) potentially opens up Pandora’s Box, in that you have disconnected events, handled by separate chunks of logic, at different points in “time”, with possible inter-dependencies. If you’re not careful about designing the event system, then subtle second and third-order effects can creep in, creating debugging nightmares.
Swing is an event-driven GUI framework. Not all event-driven
frameworks are implemented the same, although they all act like Discrete
Event Simulations. Swing has a notion of primitive event classes (like
MouseEvent
, KeyEvent
) that are packets of domain-specific
information communicated to interested parties. Swing components, like a
basic JPanel
or a JButton
, serve as event sources. Listener classes,
like the MouseListener
and KeyListener
, can be attached to Swing
Components to simultaneously indicate interest in particular events, and
to provide mechanisms for handling the events.
The relationship between components and listeners is a form of the Observer design pattern. The observer pattern allows programmers to define relationships between an observable, and an observer. The observable is responsible for notifying any interested (or subscribed) observers that a change has occurred. Observables typically do this by evaluating a callback function. Callbacks are usually supplied by the observer when the observer “subscribes” to the observable. This allows the propogation of information (notifications passed on from the observable) to be decoupled from the handling of information (the functionality defined in the observer). For event-driven programs, the observer pattern allows a nice decoupling between event definition, and event-handling.
From what I’ve seen, most Java folks will not mention much, if anything, about observables when talking about Swing. They generally refer to Swing idioms for the observer pattern: attach a listener to a component. If you were to talk to a .Net citizen, they might say “subscribe/add a delegate to an event.” In both cases, they talking about the observer pattern, as it underlies the architecture for both GUI implementations.
Next time: criticisms of Swing’s observer implementation, praise for .Net, and bringing First Class Events to Clojure.