Design Patterns

advertisement
Design Patterns
Introduced in Design Patterns, by Gamma, Helm, Johnson & Vlissides (Addison-Wesley, 1995)
 Authors known as the “gang of four” in the developing design patterns literature
 Evolved idea of patterns from Smalltalk frameworks and Jim Coplien’s book o C++ idioms.
 But design patterns are more abstract, less code-specific, than either frameworks or idioms
 Shows 23 different solutions to different classes of problems in C++ and Smalltalk
 Eckels maps some of these solutions in Java
Design patterns aren’t really tied to the realm of design;
Patterns stand apart from traditional stages of analysis, design and implementation
Instead, a pattern embodies a complete idea within a program,
So can appear at the analysis or high-level design phase,
Even though a pattern has a direct implementation in code.
How is this possible? How can a pattern with a code implementation show up so early?
Abstraction: a pattern is a way to separate things that change from things that stay the same
Heinz Zullighoven: “A pattern is the abstraction from a concrete form which keeps
recurring in specific non-arbitrary contexts.”
Jim Coplein: “I like to relate this definition to dress patterns. I could tell you how to make a
dress by specifying the route of a scissors through a piece of cloth in terms of angles and
lengths of cut. Or, I could give you a pattern. Reading the specification, you would have no
idea what was being built or if you had built the right thing when you were finished. The
pattern foreshadows the product: it is the rule for making the thing, but it is also, in many
respects, the thing itself.”
Discovering a use for a pattern in analysis or early, high-level design
is like discovering a large invariant; it promotes reuse and maintainability
Java has included some design pattern from early versions of its library and has added new ones
E.g., Enumeration is a pattern for modeling iteration,
Iterator is an improvement of this pattern in JDK 1.1, which works with its collections.
Iterator lets you write generic code that performs an operation on all elements
in a sequence without regard to how that sequence is built
Note emphasis on genericity in design patterns – works a little better in C++ with templates
The “Gang of Four” describe three general categories of design patterns:
 Creational patterns isolate the details of object creation (Singleton, FactoryMethod, Prototype)
 Structural patterns work with the way object connect with other objects to ensure that changes in
the system don’t reqauire changes in connections (Composite, Proxy, Bridge, Façade)
 Behavioral patterns encapsulate actions or processes (Iterator, Observer, Visitor)
1
Class singleton
Class singleton is a simple design pattern: provides one and only one instance of an object:
this is a creational design pattern
//: SingletonPattern.java -- the Singleton design pattern:
// you can never instantiate more than one.
package c16;
// Since this isn't inherited from a Cloneable base class
// and cloneability isn't added, making it final prevents
// cloneability from being added in any derived classes
final class Singleton {
//1a
private static Singleton s = new Singleton(47);
private int i;
private Singleton(int x) { i = x; }
//1b
public static Singleton getHandle() {
//2
return s;
}
public int getValue() { return i; }
public void setValue(int x) { i = x; }
}
public class SingletonPattern {
public static void main(String[] args) {
Singleton s = Singleton.getHandle();
System.out.println(s.getValue());
Singleton s2 = Singleton.getHandle();
s2.setValue(9);
System.out.println(s.getValue());
try {
// Can't do this: compile-time error.
// Singleton s3 = (Singleton)s2.clone();
} catch(Exception e) {}
}
} ///:~



A final class, with all constructors private, so that client cannot create any extras.
Method getHandle() produces a handle to the Singleton object
How does Singleton encapsulate that pattern of a Singleton object?
2
Observer pattern: a group of objects need to update themselves when some object changes state
 “Model-view” aspect of Smalltalk’s model-view-controller framework models this view
 When you change state of model, views must know to update themselves correspondingly
 class Observable keeps track of everybody who wants to informed of a change
 class Observer says “OK, everybody check and maybe update themselves.”
 Observable class calls method notifyObservers() for each one on the list
//: BoxObserver.java demonstrates of Observer pattern
// using Java's built-in observer classes.
import java.awt.*;
import java.awt.event.*;
import java.util.*;
//1
// You must inherit a new type of Observable:
class BoxObservable extends Observable {
public void notifyObservers(Object b) {
// Otherwise it won't propagate changes:
setChanged();
super.notifyObservers(b);
}
}
//2
//3
public class BoxObserver extends Frame {
Observable notifier = new BoxObservable();
//4
public BoxObserver(int grid) {
setTitle("Demonstrates Observer pattern");
setLayout(new GridLayout(grid, grid));
for(int x = 0; x < grid; x++)
for(int y = 0; y < grid; y++)
add(new OCBox(x, y, notifier));
//5
}
public static void main(String[] args) {
int grid = 8;
if(args.length > 0)
grid = Integer.parseInt(args[0]);
Frame f = new BoxObserver(grid);
f.setSize(500, 400);
f.setVisible(true);
f.addWindowListener(
//6
new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
}
3
class OCBox extends Canvas implements Observer {
Observable notifier;
int x, y; // Locations in grid
Color cColor = newColor();
static final Color[] colors = {
Color.black, Color.blue, Color.cyan,
Color.darkGray, Color.gray, Color.green,
Color.lightGray, Color.magenta,
Color.orange, Color.pink, Color.red,
Color.white, Color.yellow
};
static final Color newColor() {
return colors[
(int)(Math.random() * colors.length)
];
}
OCBox(int x, int y, Observable notifier) {
this.x = x;
this.y = y;
notifier.addObserver(this);
this.notifier = notifier;
addMouseListener(new ML());
}
public void paint(Graphics g) {
g.setColor(cColor);
Dimension s = getSize();
g.fillRect(0, 0, s.width, s.height);
}
class ML extends MouseAdapter {
public void mousePressed(MouseEvent e) {
notifier.notifyObservers(OCBox.this);
}
}
public void update(Observable o, Object arg) {
OCBox clicked = (OCBox)arg;
if(nextTo(clicked)) {
cColor = clicked.cColor;
repaint();
}
}
private final boolean nextTo(OCBox b) {
return Math.abs(x - b.x) <= 1 &&
Math.abs(y - b.y) <= 1;
}
} ///:~
4
//7
//8
//9
//10
//11
//12
//13
Comments on BoxObserver code:
1. Package java.util contains class Observer and interface Observer
2. Method setchanged sets “changed” flag so observers got notified
3. super.notifyObservers(b) invokes base class Observable’s notifyObservers() method
4. Constructor creates a single Observable object called notifier
5. Create a matrix of objects of type OCbox (defined below), passing in coordinates and notifier.
6. BTW, this anonymous inner class defines a WindowAdapter (much shorter than defining every
method in the WindowListener interface), thus just defines windowClosing() method
7. Class OCbox implements interface Observer
8. OCbox’s constructor add’s this OCbox to notifier’s list.
9. When mouse is clicked, the notifierObservers() method is called, passing in clicked object
10. Method update() is part of Observer interface, here defined by OCbox
11. If the clicked Observable object is nextTo this Observer object,
12. then change the color of this Observer to color of Observable object
13. Here’s the definition of method nextTo()
Many generic design patterns seek to remove dependency on RTTI -- why?
 To avoid cascaded if type-checking pattern which weakens extensibility and maintainability
 RTTI is generally “considered harmful” (Stroustrup tried hard to keep it out of C++)
 Hard to do in Java without genericity
 Eckel provides an example that uses RTTI in a way that promotes extensibility
 Idea of this recycling example is to sort different types of trash into the right bins (vectors)
1) Class TypeMap associates different types of trash with a type of trash bin (Vector)
2) HashTable implements the association of types with bins
3) Java RTTI: getClass returns string representation of a class using
4) If HashTable t contains this string, then add this trash object o to associated bin (vector)
5) Else create a new vector (trash bin) associated with this type of object (o) key
6) Method get() returns Vector (bin) associated with a class type
7) Method keys() returns an Enumeration of the keys in Hashtable t
8) Method filler() implements interface fillable by implementing addTrash()
9) Invokes method fillbin() of class ParseTrash to parse trash objects from a file
10) Class ParseTrash uses the Factory design pattern (supplied with JDK 1.1) to map a string
into a class type (for some type of trash)
Here’s how JDK documentation describes Factory design pattern:
“A factory implementation is useful when you need one object to control the creation of and/or access
to other object. When you go to the bank to make a deposit to your account, you don't walk up to a
vault, pull out a drawer with your name on it, drop in your money, shut the drawer and leave. Think
about how you originally established, or opened, the account. You probably went to the bank, spoke
with an Account Manager, and signed some papers. In return, they gave you some checks, a passbook,
or a bank card so you could access your account in the future.
The Account Manager is an example of a factory. The person or Automated Teller Machine (ATM)
that acts as account manager controls the creation of and/or access to individual accounts.”
Eckert supplies his own simple version of Factory() which returns a handle to a new object
5
//: DynaTrash.java
// Using a Hashtable of Vectors and RTTI to automatically sort trash
// into vectors.
// This solution, despite the use of RTTI, is extensible.
package c16.dynatrash;
import c16.trash.*;
import java.util.*;
// Generic TypeMap works in any situation:
class TypeMap {
private Hashtable t = new Hashtable();
public void add(Object o) {
Class type = o.getClass();
if(t.containsKey(type))
((Vector)t.get(type)).addElement(o);
else {
Vector v = new Vector();
v.addElement(o);
t.put(type,v);
}
}
public Vector get(Class type) {
return (Vector)t.get(type);
}
public Enumeration keys() { return t.keys(); }
// Returns handle to adapter class to allow
// callbacks from ParseTrash.fillBin():
public Fillable filler() {
// Anonymous inner class:
return new Fillable() {
public void addTrash(Trash t) { add(t); }
};
}
}
//1
//2
//3
//4
//5
//6
//7
//8
public class DynaTrash {
public static void main(String[] args) {
TypeMap bin = new TypeMap();
ParseTrash.fillBin("Trash.dat",bin.filler()); //9
Enumeration keys = bin.keys();
while(keys.hasMoreElements())
Trash.sumValue(
bin.get((Class)keys.nextElement()));
}
} ///:~
6
//: ParseTrash.java
// Open a file and parse its contents into
// Trash objects, placing each into a Vector
package c16.trash;
import java.util.*;
import java.io.*;
public class ParseTrash {
public static void
fillBin(String filename, Fillable bin) {
try {
BufferedReader data =
new BufferedReader(
new FileReader(filename));
String buf;
while((buf = data.readLine())!= null) {
String type = buf.substring(0,
buf.indexOf(':')).trim();
double weight = Double.valueOf(
buf.substring(buf.indexOf(':') + 1)
.trim()).doubleValue();
bin.addTrash(
Trash.factory(
new Trash.Info(type, weight)));
}
data.close();
} catch(IOException e) {
e.printStackTrace();
} catch(Exception e) {
e.printStackTrace();
}
}
// Special case to handle Vector:
public static void
fillBin(String filename, Vector bin) {
fillBin(filename, new FillableVector(bin));
}
} ///:~
7
//10
Summary: design patterns in general strive to separate the things that change from the things
that remain the same.
Thus, OOP isn’t just about polymorphism; polymorphism is just one way
“to separate the things that change from the things that remain the same.”
Design patterns show other ways to accomplish this goal of genericity.
Some useful links:
http://st-www.cs.uiuc.edu/users/patterns (a page of links to tutorials, articles, conferences, etc.)
http://www.enteract.com/~bradapp/docs/patterns-intro.html (Brad Appleton’s introduction)
http://www.bell-labs.com/people/cope/Patterns/Process/index.html (Jim Coplien’s work on patterns)
8
Download