Common GUI patterns
A handful of arrangements that turn up in every desktop application.
Observer
The one every toolkit is built on. A component announces that something happened; interested parties have registered to hear it.
button.addActionListener(e -> doSomething());
The button knows nothing about what it triggers. That is why one button can drive several things, and why the same handler can serve a button and a menu item.
Command
Wrap an operation in an object so it can be attached to several controls and enabled or disabled in one place. Swing has this built in as Action:
Action save = new AbstractAction("Save") {
public void actionPerformed(ActionEvent e) { document.save(); }
};
saveButton.setAction(save);
saveMenuItem.setAction(save);
save.setEnabled(false); // both go grey
Sharing one Action between a toolbar button and a menu item is the standard use, and it removes an entire class of bug where the two get out of step.
Composite
Containers hold components, and a container is itself a component. So a panel can hold panels, and layout nests arbitrarily. This is why building a complex window out of small, individually simple panels works better than one enormous layout.
The single-thread rule
Swing components may be touched only on the Event Dispatch Thread. Two consequences:
Starting the interface goes through the EDT:
SwingUtilities.invokeLater(() -> new MainWindow().setVisible(true));
And anything slow must leave it:
new SwingWorker<Result, Void>() {
protected Result doInBackground() { return loadData(); }
protected void done() { table.setModel(new Model(get())); }
}.execute();
doInBackground runs off the EDT; done runs on it. Work done directly in a listener freezes the window until it finishes — the single most common defect in desktop Java, and instantly recognisable to users as an application that has “hung”.
Model-view separation
Give components a model rather than pushing values into them one at a time. JTable with a proper TableModel sorts, filters and refreshes itself. Building a table by adding rows imperatively works until the data changes, and then it is all manual.