25
Design Patterns I 1

Design Patterns I 1. Creational Pattern Singleton: intent and structure Ensure a class has one instance, and provide a global point of access to it 2

  • View
    216

  • Download
    0

Embed Size (px)

Citation preview

Design PatternsI

1

Creational PatternSingleton: intent and structure

Ensure a class has one instance, and provide a global point of access to it

Ensure a class has one instance, and provide a global point of access to it

2

Singleton: implementation

/*Singleton*/class Keyboard{

private static Keyboard instance_ = null;

public static Keyboard instance() {if (instance_ = null)

instance_ = new Keyboard();return instance_;

}

private Keyboard() { … }…

}

/*Singleton*/class Keyboard{

private static Keyboard instance_ = null;

public static Keyboard instance() {if (instance_ = null)

instance_ = new Keyboard();return instance_;

}

private Keyboard() { … }…

}

3

Singleton: usageUse 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

4

Creational Pattern Factory Method: intent and structureDefine an interface for creating an object, but let

subclasses decide which class to instantiate.Define an interface for creating an object, but let

subclasses decide which class to instantiate.

5

Factory Method: implementation/* Product */class Document {…}

/* Concrete Product */class PDFDocument extends Document {…}

/* Creator */abstract class Application {

ArrayList<Document> docs = new ArrayList<Document>();public abstract Document createDocument();public void newDocument() {

Document d = createDocument();docs.add(d);

}}/* Concrete Creator */class PDFApplication extends Application {

public PDFDocument createDocument() {return new PDFDocument();

}}

/* Product */class Document {…}

/* Concrete Product */class PDFDocument extends Document {…}

/* Creator */abstract class Application {

ArrayList<Document> docs = new ArrayList<Document>();public abstract Document createDocument();public void newDocument() {

Document d = createDocument();docs.add(d);

}}/* Concrete Creator */class PDFApplication extends Application {

public PDFDocument createDocument() {return new PDFDocument();

}}

6

Factory Method: usageUse 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

7

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.

Separate the construction of a complex object from its representation so that the same construction process

can create different representations.

8

Builder: implementation/*Product*/class Pizza {…}/* Builder */abstract class PizzaBuilder {

protected Pizza p;public Pizza getPizza() { return p;}public abstract void createNewPizza();public abstract void buildSauce(); public abstract void buildTopping();

}/* Concrete Builder */class SpicyPizzaBuilder extends PizzaBuilder {

public void createNewPizza() {p = new Pizza();

}public void buildSauce() {

p.setSauce("hot"); } public void buildTopping() {

p.setTopping("pepperoni"); }

}

/*Product*/class Pizza {…}/* Builder */abstract class PizzaBuilder {

protected Pizza p;public Pizza getPizza() { return p;}public abstract void createNewPizza();public abstract void buildSauce(); public abstract void buildTopping();

}/* Concrete Builder */class SpicyPizzaBuilder extends PizzaBuilder {

public void createNewPizza() {p = new Pizza();

}public void buildSauce() {

p.setSauce("hot"); } public void buildTopping() {

p.setTopping("pepperoni"); }

}9

Builder: implementation (cont.)/* Director */class Cook {

private PizzaBuilder pb;public void setPizzaBuilder(PizzaBuilder pb_) {pb = pb_;}public Pizza getPizza() {return pb.getPizza();}public void constructPizza() {

pb.createNewPizza (); pb.buildSauce();pb.buildTopping();

}}/* User */public class Main {

public static void main(String[] args) {Cook c = new Cook();PizzaBuilder spicyPB = new SpicyPizzaBuilder(); c.setPizzaBuilder(spicyPB ); c.constructPizza();Pizza p = cook.getPizza();

}}

/* Director */class Cook {

private PizzaBuilder pb;public void setPizzaBuilder(PizzaBuilder pb_) {pb = pb_;}public Pizza getPizza() {return pb.getPizza();}public void constructPizza() {

pb.createNewPizza (); pb.buildSauce();pb.buildTopping();

}}/* User */public class Main {

public static void main(String[] args) {Cook c = new Cook();PizzaBuilder spicyPB = new SpicyPizzaBuilder(); c.setPizzaBuilder(spicyPB ); c.constructPizza();Pizza p = cook.getPizza();

}}

10

Builder: usageUse 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

11

Structural Pattern Flyweight: intent

Use sharing to support large numbers of fine-grained objects efficiently.

Use sharing to support large numbers of fine-grained objects efficiently.

12

Flyweight: structure

13

Flyweight : structureFlyweight  -

- 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).

14

Flyweight : implementation/* Concrete flyweight */class Coordinate { public Coordinate(int x_){x = x_;} public void report(int y) { System.out.println("("+x+","+y+")"); } private int x;}

/* Flyweight Factory */class Factory{ public static Coordinate getFly(int x) {

if (pool[x] == null) pool[x] = new Coordinate(x); return pool[x]; } public static int numX = 6; public static int numY = 10; private static Coordinate pool[] = new Coordinate[numX];}

/* Concrete flyweight */class Coordinate { public Coordinate(int x_){x = x_;} public void report(int y) { System.out.println("("+x+","+y+")"); } private int x;}

/* Flyweight Factory */class Factory{ public static Coordinate getFly(int x) {

if (pool[x] == null) pool[x] = new Coordinate(x); return pool[x]; } public static int numX = 6; public static int numY = 10; private static Coordinate pool[] = new Coordinate[numX];}

15

Flyweight : implementation (cont.)/* User */class Main {

public static void 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); }}

}

/* User */class Main {

public static void 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); }}

}

16

Flyweight : usageUse 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

17

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.

Defines a family of algorithms, encapsulates each one, and makes them interchangeable.

Strategy lets the algorithm vary independently from the clients that use it.

18

Strategy: implementation/* Strategy */interface IResizeStrategy {

public int newLimit(int index, int size);}

/* Concrete Strategy 1 */class MinimalResizeStrategy implements IResizeStrategy {

public int newLimit(int index, int size) {return index + 1;

}}

/* Concrete Strategy 2 */class Factor2ResizeStrategy implements IResizeStrategy {

public int newLimit(int index, int size) {return index * 2 + 10;

}}

/* Strategy */interface IResizeStrategy {

public int newLimit(int index, int size);}

/* Concrete Strategy 1 */class MinimalResizeStrategy implements IResizeStrategy {

public int newLimit(int index, int size) {return index + 1;

}}

/* Concrete Strategy 2 */class Factor2ResizeStrategy implements IResizeStrategy {

public int newLimit(int index, int size) {return index * 2 + 10;

}}

19

Strategy: implementation (cont.)/* Context */public class ArrayCollection { private Object[] array = new Object[0]; private IResizeStrategy rs = IResizeStrategy.MINIMAL;

public void setResizeStrategy(IResizeStrategy rs_) { rs = rs_;

} public void put(int index, 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) throw new IndexOutOfBoundsException();

return array[index]; } protected void resize(int length) {…}}

/* Context */public class ArrayCollection { private Object[] array = new Object[0]; private IResizeStrategy rs = IResizeStrategy.MINIMAL;

public void setResizeStrategy(IResizeStrategy rs_) { rs = rs_;

} public void put(int index, 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) throw new IndexOutOfBoundsException();

return array[index]; } protected void resize(int length) {…}}

20

Strategy: usageUse 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 interface21

Behavioral Pattern State: intent and structure

22

Allow an object to alter its behavior when its internal state changes. The object will appear to change its

class.

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 TCPClosed extends TCPState { public static TCPState instance(){…} public void open(TCPConnection c){

changeState(c, TCPEstablished.instance()); }}/* Concrete State 2 */class TCPEstablished extends TCPState { public static TCPState instance(){…} public void close(TCPConnection c) ){

changeState(c, TCPClosed.instance()); }}

/* 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 TCPClosed extends TCPState { public static TCPState instance(){…} public void open(TCPConnection c){

changeState(c, TCPEstablished.instance()); }}/* Concrete State 2 */class TCPEstablished extends TCPState { public static TCPState instance(){…} public void close(TCPConnection c) ){

changeState(c, TCPClosed.instance()); }}

23

State: implementation (cont.)/* Context */class TCPConnection { private TCPState s; public TCPConnection(); public void open(){

s.open(this); } public void close() {

s.close(this); } public void changeState(TCPState s_){

s = s_; } }

/* Context */class TCPConnection { private TCPState s; public TCPConnection(); public void open(){

s.open(this); } public void close() {

s.close(this); } public void changeState(TCPState s_){

s = s_; } }

24

State: usageUse 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.

25