260 likes | 278 Views
Design Patterns I. Creational Pattern Singleton: intent and structure. Ensure a class has one instance, and provide a global point of access to it. Singleton: implementation. /*Singleton*/ class Keyboard { private static Keyboard instance_ = null ;
E N D
Creational PatternSingleton: intent and structure Ensure a class has one instance, and provide a global point of access to it
Singleton: implementation /*Singleton*/ class Keyboard { privatestatic Keyboard instance_ = null; publicstatic Keyboard instance() { if (instance_ = null) instance_ = new Keyboard(); return instance_; } private Keyboard() { … } … }
Singleton: usage • Use the singleton pattern when: • There must be exactly one instance of a class, and it must be accessible to clients from a well-known access point. • When the sole instance should be extensible by subclassing, and clients should be able to use an extended instance without modifying their code. • Consequences: • Controlled access to a sole instance. • An elegant replacement for global variables. • More flexible than static member functions • Allows overriding • The singleton may be extended to allow several instances
Creational Pattern Factory Method: intent and structure Define an interface for creating an object, but let subclasses decide which class to instantiate.
Factory Method: implementation /* Product */ class Document {…} /* Concrete Product */ classPDFDocumentextends Document {…} /* Creator */ abstractclass Application { ArrayList<Document> docs = newArrayList<Document>(); publicabstract Document createDocument(); publicvoidnewDocument() { Document d = createDocument(); docs.add(d); } } /* Concrete Creator */ classPDFApplicationextends Application { publicPDFDocumentcreateDocument() { returnnewPDFDocument(); } }
Factory Method: usage • Use the factory method pattern when: • A class can’t anticipate the class of objects it must create • A class wants its subclasses to specify the object it creates • Classes delegate responsibility to one of several helper subclasses, and you want to localize the knowledge of which helper subclass is the delegate • Consequences: • Eliminates the needs to bind application classes into your code. • Enables the subclasses to provide an extended version of an object. • Allows to connects parallel class hierarchies
Creational Pattern Builder: intent and structure Separate the construction of a complex object from its representation so that the same construction process can create different representations.
Builder: implementation /*Product*/ class Pizza {…} /* Builder */ abstractclassPizzaBuilder { protected Pizza p; public Pizza getPizza() { return p;} publicabstractvoidcreateNewPizza(); publicabstractvoidbuildSauce(); publicabstractvoidbuildTopping(); } /* Concrete Builder */ classSpicyPizzaBuilderextendsPizzaBuilder { publicvoidcreateNewPizza() { p = new Pizza(); } publicvoidbuildSauce() { p.setSauce("hot"); } publicvoidbuildTopping() { p.setTopping("pepperoni"); } }
Builder: implementation (cont.) /* Director */ class Cook { privatePizzaBuilderpb; publicvoidsetPizzaBuilder(PizzaBuilderpb_) {pb = pb_;} public Pizza getPizza() {returnpb.getPizza();} publicvoidconstructPizza() { pb.createNewPizza (); pb.buildSauce(); pb.buildTopping(); } } /* User */ publicclass Main { publicstaticvoid main(String[] args) { Cook c = new Cook(); PizzaBuilderspicyPB = newSpicyPizzaBuilder(); c.setPizzaBuilder(spicyPB); c.constructPizza(); Pizza p = cook.getPizza(); } }
Builder: usage • Use the builder pattern when: • The algorithm for creating a complex object should be independent of the parts that make up the object and how they're assembled. • The construction process must allow different representations for the object that's constructed • Consequences: • Isolates code for construction and representation • Construction logic is encapsulated within the director • Product structure is encapsulated within the concrete builder • => Lets you vary a product's internal representation • Supports fine control over the construction process • Breaks the process into small steps
Structural Pattern Flyweight: intent Use sharing to support large numbers of fine-grained objects efficiently.
Flyweight : structure • Flyweight - • - declares an interface through which flyweights can receive and act on extrinsic state. • ConcreteFlyweight - • - implements the Flyweight interface and adds storage for intrinsic state, if any. A ConcreteFlyweight object must be sharable. Any state it stores must be intrinsic, that is, it must be independent of the ConcreteFlyweight object's context. • UnsharedConcreteFlyweight - • - not all Flyweight subclasses need to be shared. The Flyweight interface enables sharing, but it doesn't enforce it. It is common for UnsharedConcreteFlyweight objects to have ConcreteFlyweight objects as children at some level in the flyweight object structure. • FlyweightFactory - • - creates and manages flyweight objects. • - ensures that flyweight are shared properly. When a client requests a flyweight, the FlyweightFactory objects supplies an existing instance or creates one, if none exists. • Client - • - maintains a reference to flyweight(s). • - computes or stores the extrinsic state of flyweight(s).
Flyweight : implementation /* Concrete flyweight */ class Coordinate { public Coordinate(int x_){x = x_;} publicvoid report(int y) { System.out.println("("+x+","+y+")"); } privateint x; } /* Flyweight Factory */ class Factory { publicstatic Coordinate getFly(int x) { if (pool[x] == null) pool[x] = new Coordinate(x); return pool[x]; } public static intnumX = 6; public static intnumY = 10; private static Coordinate pool[] = new Coordinate[numX]; }
Flyweight : implementation (cont.) • /* User */ • class Main { • publicstaticvoid main(String[] args){ • for (int x = 0; x < Factory.numX; ++x) { • for (int y = 0; y < Factory.numY; ++y) • Factory.get_fly(x).report(y); • } • } • }
Flyweight : usage • Use the flyweight pattern when all conditions are true: • The application uses large number of objects. • Storage costs are high because of the quantity of objects. • Most object state can be made extrinsic. • Many groups of objects may be replaced by relatively few shared objects once extrinsic state is removed. • The application doesn't depend on object identity. • Consequences: • Reduction in the number of objects to handle • Reduction in memory and storage devices if the objects are persisted
Behavioral Pattern Strategy: intent and structure Defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently from the clients that use it.
Strategy: implementation /* Strategy */ interfaceIResizeStrategy { public intnewLimit(int index, int size); } /* Concrete Strategy 1 */ classMinimalResizeStrategyimplementsIResizeStrategy { publicintnewLimit(int index, int size) { return index + 1; } } /* Concrete Strategy 2 */ class Factor2ResizeStrategy implementsIResizeStrategy { publicintnewLimit(intindex, int size) { return index * 2 + 10; } }
Strategy: implementation (cont.) /* Context */ publicclassArrayCollection { private Object[] array = new Object[0]; privateIResizeStrategyrs = IResizeStrategy.MINIMAL; publicvoidsetResizeStrategy(IResizeStrategyrs_) { rs = rs_; } publicvoid put(intindex, Object o) { if (index >= array.length) { int length = rs.newLimit(index, array.length); resize(length); } array[index] = o; } public Object get(int index) { if (index >= array.length) thrownewIndexOutOfBoundsException(); return array[index]; } protectedvoid resize(int length) {…} }
Strategy: usage • Use the strategy pattern when: • Many related classes differ only in their behavior • Different variants of an algorithm are required • An algorithm uses data that clients shouldn't know about • A class defines many behaviors, and these appear as multiple conditional statements in its operations. • Consequences: • Defines family of related algorithms • Provides an alternative to subclassing the Context class • Eliminates large conditional statements • Provides a choice of implementations for the same behavior • Increases the number of objects • Client must be aware of different strategies • All algorithms must use the same Strategy interface
Behavioral Pattern State: intent and structure Allow an object to alter its behavior when its internal state changes. The object will appear to change its class.
State: implementation /* State */ class TCPState { public void open(TCPConnection c){…}; public void close(TCPConnection c){…}; protected void changeState(TCPConnection c, TCPState s){ c.changeState(s); } } /* Concrete State 1 */ class TCPClosedextends TCPState{ publicstaticTCPState instance(){…} public void open(TCPConnection c){ changeState(c, TCPEstablished.instance()); } } /* Concrete State 2 */ class TCPEstablishedextends TCPState{ publicstaticTCPState instance(){…} public void close(TCPConnection c) ){ changeState(c, TCPClosed.instance()); } }
State: implementation (cont.) /* Context */ classTCPConnection { private TCPState s; publicTCPConnection(); public void open(){ s.open(this); } public void close() { s.close(this); } public void changeState(TCPState s_){ s = s_; } }
State: usage • Use the state pattern when: • An object's behavior depends on its state, and it must change its behavior at run-time depending on that state. • Operations have large, multipart conditional statements that depend on the object's state. • Consequences: • It localizes state-specific behavior and partitions behavior for different states. • It makes state transitions explicit. • State objects can be shared.