10/21/2011
Prototype pattern
Every object is itself a factory Each class contains a clone method that creates a copy of the receiver object class Bicyle { Bicycle clone() { ... } } Often, Object is the return type of clone CSE 331 clone is declared in Object Design flaw in Java 1.4 and earlier: the return type may SOFTWARE DESIGN & IMPLEMENTATION not change covariantly in an overridden method DESIGN PATTERNS II That is, the return type could not be made more restrictive This is a problem for achieving true subtyping
Autumn 2011
Using prototypes Dependency injection
class Race { Change the factory without changing the code with external Bicycle bproto; dependency injection // constructor BicycleFactory f = ((BicycleFactory) DependencyManager.get("BicycleFactory")); Race(Bicycle bproto) { this.bproto = bproto; } Race r = new TourDeFrance(f); Race createRace() { Plus an external file Bicycle bike1 = (Bicycle) bproto.clone();
+ Change the factory without recompiling Again, we can specify the race and the bicycle separately - Harder to understand (for example, new TourDeFrance(new Tricycle()) without changing any Java code the program might call a different factory)
A brief aside: call graphs Precision
A call graph is a set of pairs describing, for a given program, which units Of course, there’s an easy algorithm to create a not-very-useful static call (usually methods) call other units (usually methods) graph for (m : method) Eclipse, for example, has a Call Hierarchy view (where the Callee for (n : method) Hierarchy option is often best) that is at times useful in programming include
UW CSE331 Autumn 2011 UW CSE331 Autumn 2011
1 10/21/2011
Sharing Interning pattern
Reuse existing objects instead of creating new ones Interning: only one object with a particular (abstract) value exists at run-time Less space May compare with == instead of equals()
Factory method returns an existing object, not a new Permitted only for immutable objects
one 1-100 (Street- NumberSet) Flyweight: separate intrinsic and extrinsic state, "Univ. Way" (Street- "Univ. Way" (String) represent them separately, and intern the intrinsic Segment) (String) StreetSegment with interning 1-100 "O2139" (Street- state (String) NumberSet) StreetSegment (Street- Segment) without interning 101-200 101-200 (Street- Implicit representation uses no space (Street- NumberSet) NumberSet) (Street- Segment) (Street- "Univ. Way" Segment) (String) "O2139" (String)
"O2139" (String)
java.lang.Boolean does not use Interning mechanism the Interning pattern
public class Boolean { Maintain a collection of all objects private final boolean value; If an object already appears, return that instead // construct a new Boolean value HashMap
Recognition of the problem Structural patterns: Wrappers
Javadoc for Boolean constructor A wrapper translates between incompatible interfaces Allocates a Boolean object representing the value argument Wrappers are a thin veneer over an encapsulated class Note: It is rarely appropriate to use this constructor. Unless a new modify the interface instance is required, the static factory valueOf(boolean) is generally a better choice. It is likely to yield significantly better extend behavior space and time performance restrict access
Josh Bloch (JavaWorld, January 4, 2004) The encapsulated class does most of the work The Boolean type should not have had public constructors. Wrapper Functionality Interface There's really no great advantage to allow multiple trues or Pattern multiple falses, and I've seen programs that produce millions of trues and millions of falses, creating needless work for the Adapter same different garbage collector Decorator different same So, in the case of immutables, I think factory methods are great Proxy same same
2 10/21/2011
Adapter Adapting via subclassing
interface Rectangle { class ScaleableRectangle1 extends NonScaleableRectangle Change an interface without // grow or shrink by the given factor implements Rectangle { changing functionality void scale(float factor); ... void scale(float factor) { rename a method float getWidth(); float area(); setWidth(factor * getWidth()); convert units } setHeight(factor * getHeight()); class myClass { implement a method in } terms of another void myMethod(Rectangle r) { ... r.scale(2); ... } } Example } Have the Rectangle class on class NonScaleableRectangle { the top right void setWidth(float width) { ... } void setHeight(float height) { ... } Want to be able to use the // no scale method NonScaleableRectangle ... class on the bottom right, which } is not a Rectangle
Adapting via delegation: Subclassing vs. delegation Forwarding requests to another object
class ScaleableRectangle2 implements Rectangle { Subclassing NonScaleableRectangle r; ScaleableRectangle2(NonScaleableRectangle r) { automatically gives access to all methods of superclass this.r = r; built into the language (syntax, efficiency) } Delegation void scale(float factor) { permits cleaner removal of methods (compile-time checking) setWidth(factor * r.getWidth()); setHeight(factor * r.getHeight()); wrappers can be added and removed dynamically } objects of arbitrary concrete classes can be wrapped
float getWidth() { return r.getWidth(); } multiple wrappers can be composed float circumference() { return r.circumference(); } Some wrappers have qualities of more than one of ... } adapter, decorator, and proxy
Decorator Decorator: Bordered windows
interface Window { Add functionality without changing the interface // rectangle bounding the window Add to existing methods to do something additional Rectangle bounds(); (while still preserving the previous specification) // draw this on the specified screen Not all subclassing is decoration void draw(Screen s); ... }
class WindowImpl implements Window { ... }
3 10/21/2011
Bordered window implementations A decorator can remove functionality class BorderedWindow1 extends WindowImpl { void draw(Screen s) { Remove functionality without changing the interface super.draw(s); Subclassing Example: UnmodifiableList bounds().draw(s); } What does it do about methods like add and put? } class BorderedWindow2 implements Window { Window innerWindow; BorderedWindow2(Window innerWindow) { this.innerWindow = innerWindow; } void draw(Screen s) { Delegation permits innerWindow.draw(s); innerWindow.bounds().draw(s); multiple borders, borders } and/or shading, etc. }
Proxy Visitor pattern
Visitor encodes a class Node { Same interface and functionality as the wrapped class traversal of a void accept(Visitor v) { Control access to other objects hierarchical data for each child of node { structure child.accept(v); communication: manage network details when using a Nodes – objects in the } remote object hierarchy – accept visitors; visitors visit v.visit(this); locking: serialize access by multiple clients nodes } } security: permit access only if proper credentials n.accept(v) performs a depth-first creation: object might not yet exist (creation is expensive) traversal of the class Visitor { void visit(Node n) { hide latency when creating object structure rooted at n, performing v's // perform work on n avoid work if object is never used operation on each } element of the } structure
Sequence of calls to accept and visit Implementing visitor
a.accept(v) You must add definitions of visit and accept a b.accept(v) visit might count nodes, perform typechecking, d.accept(v) v.visit(d) b c etc. e.accept(v) It is easy to add operations (visitors), hard to add v.visit(e) d e f v.visit(b) nodes (modify each existing visitor) c.accept(v) Visitors are similar to iterators: each element of the f.accept(v) data structure is presented in turn to the visit v.visit(f) v.visit(c) method v.visit(a) Visitors have knowledge of the structure, not just the
sequence Sequence of calls to visit:
4 10/21/2011
Next steps
Assignment 3: due Sunday October 30, 11:59PM
Lectures
M (Patterns III/GUI) W (Midterm review, including example questions)
Upcoming: Friday 10/28, in class midterm – open book, open note, closed neighbor, closed electronic devices
UW CSE331 Autumn 2011 UW CSE331 Autumn 2011
5