Slide 1
Object-Oriented Design Patterns part II
and a short introduction to GUI
Slide 2
Lesson Overview
Topics covered
(1) The Composite Design Pattern (2) The Adapter Design Pattern
(3) A short introduction to GUI components
(4) Event Handling Mecahnisms
Slide 3
The Composites Design Pattern
Intent
Compose objects into tree structures to represent part- whole hierarchies.
Composite lets clients treat individual objects and compositions of objects uniformly.
This is called recursive composition.
Slide 4
Composite Design Pattern (cont.)
Motivation
Graphics application like drawing editors let the user build complex diagrams out of simple components.
The user can group components to form larger
components, which in turn can be grouped to form still larger components.
A simple solution is to define classes for graphical
primitives such as Text and Lines plus other classes that
act as containers for these primitives. (bad idea, why?)
Slide 5
Composite Design Pattern (cont.)
Motivation
Code that uses these classes must treat primitives and container objects differentlyl, even if most of the time the user treats them identically.
Having to distinguish these objects makes the application more complex.
The Composite design pattern describes how to use
recursive composition so clients don't have to make this
distinction.
Slide 6
Composite Design Pattern – Motiviation (cont.)
The key is an abstract class that represents both primitives and containers.
For the graphic system, this class is Graphic: which declares
operations like Draw that are specific for graphical objects, and
also operations that all composite objects share, like operations
for accessing and manging its children.
Slide 7
Composite Design Pattern – Motiviation (cont.)
The Picture class allows to define aggregate Graphic objects.
Picture implements Draw to call the Draw method on its children, and it implements child-related operations accordingly.
Since Picture conforms to the Graphic interface, we can compose
Picture objects recursively:
Slide 8
Composite Design Pattern
Applicability
Use the Composite Pattern when
You want to represent part-whole hierarchies of objects.
You want clients to be able to ignore the difference between compositions of objects and individual objects. Clients will
treat all objects in the composite structure uniformly.
Slide 9
Composite Design Pattern
Structure
Slide 10
Composite Design Pattern
Collaborations
Clients use the Component class interface to interact with objects in the composite structure.
If the recipient is a Leaf, then the request is handled directly.
If it is a Composite, then it usually forwards requests to its
child components, possibly performing additional operations
before and/or after forwarding
Slide 11
Composite Design Pattern
Consequences
Benefits:
Makes it easier to add new kinds of components – does not require change in the client code, and will also easily fit into existing structures.
Makes the clients simpler since they do not have to know if they are dealing with a leaf or a composite component –
simplifies client code.
Liabilities:
Can make the design overtly general – harder to restrict the
type of components of a composite.
Slide 12
Composite Design Pattern
Implementation Issues
Explicit Parent References: A composite object knows its
contained components, that is, its children. Should components maintain a reference to their parent component?
➔
Answer: Depends on application, but having these references
supports the Chain of Responsibility pattern.
Slide 13
Composite Design Pattern
Implementation Issues (cont.)
Child Management : Where should the child management methods (add(), remove(), getChild()) be declared?
➔
Option 1: In the Component class: Gives transparency, since all components can be treated the same. But it's not safe, since clients can try to do meaningless things to leaf
components at run-time.
➔
Option 2: In the Composite class: Gives safety, since any
attempt to perform a child operation on a leaf component will be caught at compile-time. But we lose transparency, since now leaf and composite components have different
interfaces.
Slide 14
Composite Design Pattern
Implementation Issues (cont.)
Child Management : Transparent Vs. Safe:
Slide 15
Composite Design Pattern
Implementation Issues (cont.)
Component Management : Should Component maintain the list of components that will be used by a composite object? That is, should this list be an instance variable of Component rather than Composite?
➔
Better to keep this part of Composite and avoid wasting the space in every leaf object.
Child Ordering: Is child ordering important?
➔
Depends on application
Slide 16
Composite Design Pattern
Implementation Issues (cont.)
Who should delete components?
➔
Not a problem in Java! The garbage collector will come to the rescue! In languages without garbage collection, its
usually best to make a Composite responsible for deleting its children.
What's the best data structure to store components?
➔
Depends on application: a variety of data structures can be
used to store children including: linked-lists, trees, arrays
and hash-tables. Many times it isn't even necessary to use
general-purpose data structures at all.
Slide 17
Composite Example no. 1
Scenario:
A GUI system has window objects which can contain
various GUI components (widgets) such as, buttons and text areas.
A window can also contain widget container objects which can hold other widgets.
Solution 1:
Design all the widgets with different interfaces for
"updating" the screen?
Slide 18
Composite Example no. 1
We would then have to write a Window update() method as follows:
public class Window { Button[] buttons;
Menu[] menus;
TextArea[] textAreas;
WidgetContainer[] containers;
Slide 19
Composite Example no. 1
public void update() { //Class Window continued:
if (buttons != null)
for (int k = 0; k < buttons.length; k++) buttons[k].draw();
if (menus != null)
for (int k = 0; k < menus.length; k++) menus[k].refresh();
// Other widgets handled similarly.
if (containers != null)
for (int k = 0; k < containers.length; k++ ) containers[k].updateWidgets();
} ... } //end of class
Slide 20
Composite Example no. 1
Solution 1:
Looks particularly bad! - It violates the Open-Closed
Principle: If we want to add a new kind of widget, we have to modify the update() method of Window to handle it.
Solution 2:
We should always try to program to an interface, So, let's
make all widgets support the Widget interface, either by
being subclasses of a Widget class or implementing a Java
Widget interface.
Slide 21
Composite Example no. 1
Now our update() method becomes:
public class Window { Widget[] widgets;
WidgetContainer[] containers;
public void update() { if (widgets != null)
for (int k = 0; k < widgets.length; k++) widgets[k].update();
if (containers != null)
for (int k = 0; k < containers.length; k++ ) containers[k].updateWidgets();
} ... }// of class
Slide 22
Composite Example no. 1
Solution 2:
That looks better, but we are still distinguishing between widgets and widget containers
Solution 3: The Composite Pattern!
Slide 23
Composite Example no. 1