1
Josh Bloch Charlie Garrod 17-214 1 Administrivia Reading due - - PowerPoint PPT Presentation
Josh Bloch Charlie Garrod 17-214 1 Administrivia Reading due - - PowerPoint PPT Presentation
Principles of Software Construction: Objects, Design, and Concurrency Part 2: Design case studies Design case study: Java Swing Josh Bloch Charlie Garrod 17-214 1 Administrivia Reading due today: UML and Patterns 26.1 and 26.4
2
17-214
Administrivia
- Reading due today: UML and Patterns 26.1 and 26.4
- Homework 4b due Thursday, October 22nd
3
17-214
Key concepts from Thursday
- Observer design pattern
- Introduction to concurrency
– Not enough synchronization: safety failure – Too much synchronization: liveness failure
- Event-based programming
- Introduction to GUIs
4
17-214
Today
- Finish introduction to GUIs
- Design case study: GUI potpourri
– Strategy – Template method – Observer – Composite – Decorator – Adapter – Façade – Command – Chain of responsibility
- Design discussion: Decoupling your game from your GUI
5
17-214
Examples of events in GUIs
- User clicks a button, presses a key
- User selects an item from a list, an item from a menu
- Mouse hovers over a widget, focus changes
- Scrolling, mouse wheel turned
- Resizing a window, hiding a window
- Drag and drop
- A packet arrives from a web service, connection drops, …
- System shutdown, …
6
17-214
An event-based GUI with a GUI framework
- Setup phase
– Describe how the GUI window should look – Register observers to handle events
- Execution
– Framework gets events from OS, processes events
- Your code is mostly just event handlers
GUI Framework OS Application
get event drawing commands next event event— mouse, key, redraw, …
See edu.cmu.cs.cs214.rec06.alarmclock.AlarmWindow…
7
17-214
GUI frameworks in Java
- AWT – obsolete except as a part of Swing
- Swing – widely used
- SWT – Little used outside of Eclipse
- JavaFX – Billed as a replacement for Swing
– Released 2008 – never gained traction
- A bunch of modern (web & mobile) frameworks
– e.g., Android
8
17-214
GUI programming is inherently multi-threaded
- Swing Event Dispatch Thread (EDT) handles all GUI events
– Mouse events, keyboard events, timer events, etc.
- No other time-consuming activity allowed on the EDT
– Violating this rule can cause liveness failures
9
17-214
Ensuring all GUI activity is on the EDT
- Never make a Swing call from any other thread
– “Swing calls” include Swing constructors
- If not on EDT, make Swing calls with invokeLater:
public static void main(String[] args) { SwingUtilities.invokeLater(() -> new Test().setVisible(true)); }
10
17-214
Callbacks execute on the EDT
- You are a guest on the Event Dispatch Thread!
– Don’t abuse the privilege
- If > a few ms of work to do, do it off the EDT
– javax.swing.SwingWorker designed for this purpose
11
17-214
Components of a Swing application
JButton JPanel JTextField … JFrame
12
17-214
Swing has many widgets
- JLabel
- JButton
- JCheckBox
- JChoice
- JRadioButton
- JTextField
- JTextArea
- JList
- JScrollBar
- … and more
- JFrame is the Swing Window
- JPanel (a.k.a. a pane) is the container to which you add your components
(or other containers)
13
17-214
To create a simple Swing application
- Make a window (a JFrame)
- Make a container (a JPanel)
– Put it in the window
- Add components (buttons, boxes, etc.) to the container
– Use layouts to control positioning – Set up observers (a.k.a. listeners) to respond to events – Optionally, write custom widgets with application-specific display logic
- Set up the window to display the container
- Then wait for events to arrive…
14
17-214
E.g., creating a button
// public static void main… JFrame window = … JPanel panel = new JPanel(); window.setContentPane(panel); JButton button = new JButton("Click me"); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { System.out.println("Button clicked"); } }); panel.add(button); window.setVisible(true);
panel to hold the button
15
17-214
E.g., creating a button
// public static void main… JFrame window = … JPanel panel = new JPanel(); window.setContentPane(panel); JButton button = new JButton("Click me"); button.addActionListener( (e) -> System.out.println("Button clicked") ); panel.add(button); window.setVisible(true);
panel to hold the button
16
17-214
The javax.swing.ActionListener
- Listeners are objects with callback functions
– Can be registered to handle events on widgets – All registered widgets are called if event occurs interface ActionListener { void actionPerformed(ActionEvent e); }
class ActionEvent { int when; String actionCommand; int modifiers; Object source(); int id; … }
17
17-214
Button design discussion
- Button implementation should be reusable but customizable
– Different button label, different event-handling
- Must decouple button's action from the button itself
- Listeners are separate independent objects
– A single button can have multiple listeners – Multiple buttons can share the same listener
18
17-214
Swing has many event listener interfaces
- ActionListener
- AdjustmentListener
- FocusListener
- ItemListener
- KeyListener
- MouseListener
- TreeExpansionListener
- TextListener
- WindowListener
- …
class ActionEvent { int when; String actionCommand; int modifiers; Object source(); int id; … }
interface ActionListener { void actionPerformed(ActionEvent e); }
19
17-214
Today
- Finish introduction to GUIs
- Design case study: GUI potpourri
– Strategy – Template method – Observer – Composite – Decorator – Adapter – Façade – Command – Chain of responsibility
- Design discussion: Decoupling your game from your GUI
20
17-214
The decorator pattern abounds
21
17-214
The decorator pattern abounds
UML from https://medium.com/@dholnessii/structural-design-patterns-decorator-30f5a8c106a5
22
17-214
Swing layouts
see http://docs.oracle.com/javase/tutorial/uiswing/layout/visual.html
The simplest, and default, layout. Wraps around when out of space. Like FlowLayout, but no wrapping More sophisticated layout managers
23
17-214
A naïve hard-coded implementation
- A new layout would require changing or overriding JPanel
class JPanel { protected void doLayout() { switch(getLayoutType()) { case BOX_LAYOUT: adjustSizeBox(); break; case BORDER_LAYOUT: adjustSizeBorder(); break; ... } } private adjustSizeBox() { … } }
24
17-214
A better solution: delegate the layout responsibilities
- Layout classes, e.g.:
contentPane.setLayout(new FlowLayout()); contentPane.setLayout(new GridLayout(4,2));
- Similarly, there are border classes to draw the borders, e.g.:
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
25
17-214
Another GUI design challenge: nesting containers
- A JFrame contains a JPanel, which contains a JPanel (and/or
- ther widgets), which contains a JPanel (and/or other
widgets), which contains…
26
17-214
The composite pattern
- Problem: Collection of objects has behavior similar to the
individual objects
- Solution: Have collection of objects and individual objects
implement the same interface
- Consequences:
– Client code can treat collection as if it were an individual object – Easier to add new object types – Design might become too general, interface insufficiently useful
27
17-214
Another composite pattern example
public interface Expression { double eval(); // Returns value } public class BinaryOperationExpression implements Expression { public BinaryOperationExpression(BinaryOperator operator, Expression operand1, Expression operand2); } public class NumberExpression implements Expression { public NumberExpression(double number); }
28
17-214
Recall: Creating a button
//static public void main… JFrame window = … JPanel panel = new JPanel(); window.setContentPane(panel); JButton button = new JButton("Click me"); button.addActionListener( (e) -> { System.out.println("Button clicked"); }); panel.add(button); window.setVisible(true);
29
17-214
An alternative button
class MyButton extends JButton { public MyButton() { super("Click me"); } @Override protected void fireActionPerformed(ActionEvent e) { super.fireActionPerformed(e); System.out.println("Button clicked"); } } //static public void main… JFrame window = … JPanel panel = new JPanel(); window.setContentPane(panel); panel.add(new MyButton()); window.setVisible(true);
30
17-214
Discussion: Command vs. template method patterns
//static public void main… JFrame window = … JPanel panel = new JPanel(); window.setContentPane(panel); JButton button = new JButton(“Click me”); button.addActionListener( (e) -> { System.out.println("Button clicked"); }); panel.add(button); window.setVisible(true); class MyButton extends JButton { public MyButton() { super(“Click me”); } @Override protected void fireActionPerformed(ActionEvent e) { super.fireActionPerformed(e); System.out.println("Button clicked"); } } …
31
17-214
Better use of template method: partial customization
JComponent:
32
17-214
Event propagation and deep container hierarchies
33
17-214
Event propagation and deep container hierarchies
34
17-214
Event propagation and deep container hierarchies
35
17-214
Event propagation and deep container hierarchies
36
17-214
Event propagation and deep container hierarchies
37
17-214
The chain of responsibility pattern
- Problem: You need to associate functionality within a deep
nested or iterative structure, possibly with multiple objects
- Solution: Request for functionality, pass request along chain
until some component handles it
- Consequences:
– Decouples sender from receiver of request – Can simplify request-handling by handling requests near root of hierarchy – Handling of request not guaranteed
38
17-214
The design of JList and JTree
- Highly flexible rendering of lists and trees
– Can change rendering of cells – Can change source of data to display // example of simple use String [] items = { "a", "b", "c" }; JList<String> list = new JList<>(items);
39
17-214
Using JLists with a ListModel
- Allows a list widget (the view) to react to changes in the model
// with a ListModel ListModel<String> model = new DefaultListModel<>(); model.addElement("a"); JList<String> list = new JList<>(model); interface ListModel<T> { int getSize(); T getElementAt(int index); void addListDataListener(ListDataListener l); void removeListDataListener(ListDataListener l); }
40
17-214
Using JLists with a ListModel
- Allows a list widget (the view) to react to changes in the model
// with a ListModel ListModel<String> model = new DefaultListModel<>(); model.addElement("a"); JList<String> list = new JList<>(model); interface ListModel<T> { int getSize(); T getElementAt(int index); void addListDataListener(ListDataListener l); void removeListDataListener(ListDataListener l); } interface ListDataListener extends EventListener { void intervalAdded(…); void intervalRemoved(…); void contentsChanged(…); }
41
17-214
Attaching a data source to a JList
- Assume we have an anagram generator, and we want to update
a JList with new anagrams as they are generated
// design 1 class AnagramGen implements ListModel<String> { List<String> items … int getSize() { return items.size(); } String getElementAt(int index) { items.get(index).toString(); } void addListDataListener(ListDataListener l) {…} … }
42
17-214
Attaching a data source to a JList
- Assume we have an anagram generator, and we want to update
a JList with new anagrams as they are generated
// design 2 class AnagramGen { DefaultListModel<String> items … public ListModel<String> getListModel() { return items; } public Iterable<String> getItems() { return items.elements(); } … }
43
17-214
Attaching a data source to a JList
- Assume we have an anagram generator, and we want to update
a JList with new anagrams as they are generated
// design 3 class AnagramAdapter implements ListModel<String> { private final AnagramGen an; public AnagramAdapter(AnagramGen s) {an = s;} int getSize() { return count(an.getWords()); } String getElementAt(int index) { find(an.getWords(), index).toString(); } void addListDataListener(ListDataListener l) {…} … }
44
17-214
Comparing the three proposed designs
+getItems()
- items
AnagramGen JList +getSize() +getElementAt() AnagramAdapter +getSize() +getElementAt() «interface» ListModel
+getItems() AnagramGen JList +getSize() +getElementAt() DefaultListModel 1 1
+getItems() +getSize() +getElementAt()
- items
AnagramGen JList +getSize() +getElementAt() «interface» ListModel
1 2 3
45
17-214
The adapter pattern
- Problem: You have a client that expects one API for a service
provider, and a service provider with a different API
- Solution: Write a class that implements the expected API,
converting calls to the service provider's actual API
- Consequences:
– Easy interoperability of unrelated clients and libraries
- Client can use unforeseen future libraries
– Adapter class is coupled to concrete service provider, can make it harder to override service provider behavior
46
17-214
The adapter pattern, illustrated
Have this and this? Use this!
47
17-214
Aside: The façade pattern
Façade √ √ √ √ √ √ √
Subsystem classes
48
17-214
The façade vs. adapter patterns
- Motivation:
– Façade: Provide simple interface for a complex API
- Façade interface is typically new
– Adapter: Match interface expected by an existing client to existing API
- Adapter interface is defined by the existing client's expectations
49
17-214
Today
- Finish introduction to GUIs
- Design case study: GUI potpourri
– Strategy – Template method – Observer – Composite – Decorator – Adapter – Façade – Command – Chain of responsibility
- Design discussion: Decoupling your game from your GUI
50
17-214
Design discussion: Decoupling your game from your GUI
51
17-214
Summary
- GUI programming is inherently multi-threaded
– Swing calls must be made on the event dispatch thread – No other significant work should be done on the EDT
- GUIs are filled with design patterns
52