design patterns – ii

55
Design Patterns – II Lecture IV

Upload: marcos

Post on 16-Feb-2016

11 views

Category:

Documents


0 download

DESCRIPTION

Design Patterns – II. Lecture IV. Singleton Pattern. Intent Ensure a class only has one instance, and provide a global point of access to it Motivation Sometimes we want just a single instance of a class to exist in the system - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: Design Patterns – II

Design Patterns – II

Lecture IV

Page 2: Design Patterns – II

Singleton Pattern

• Intent– Ensure a class only has one instance, and provide a global

point of access to it• Motivation– Sometimes we want just a single instance of a class to exist

in the system– For example, we want just one window manager. Or just one

factory for a family of products.– We need to have that one instance easily accessible– And we want to ensure that additional instances of the class

can not be created

Page 3: Design Patterns – II

Structure

•Controlled access to sole instance•Permits a variable number of instances

Page 4: Design Patterns – II

Implementation/* Class Singleton is an implementation of a class that only allows one instantiation. */

public class Singleton {// The private reference to the one and only instance.private static Singleton uniqueInstance = null;// An instance attribute.private int data = 0;

/* Returns a reference to the single instance. Creates the instance if it does not yet exist. (This is called lazy instantiation.) */public static Singleton instance() {

if(uniqueInstance == null) uniqueInstance = new Singleton();return uniqueInstance;

}

private Singleton() {}// Accessors and mutators here!}

Page 5: Design Patterns – II

Here's a test program:

public class TestSingleton {

public static void main(String args[]) {// Get a reference to the single instance of Singleton.Singleton s = Singleton.instance();// Set the data value.s.setData(34);System.out.println("First reference: " + s);System.out.println("Singleton data value is: " + s.getData());

// Get another reference to the Singleton.// Is it the same object?s = null;s = Singleton.instance();System.out.println("\nSecond reference: " + s);System.out.println("Singleton data value is: " + s.getData());

}}

Page 6: Design Patterns – II

Singleton Pattern

And the test program output:

First reference: Singleton@1cc810Singleton data value is: 34Second reference: Singleton@1cc810Singleton data value is: 34

Page 7: Design Patterns – II

Singleton• Note that the singleton instance is only created when needed.• This is called lazy instantiation.• Thought experiment: What if two threads concurrently invoke• the instance() method? Any problems?• Two instances of the singleton class could be created!• How could we prevent this? Several methods:

– Make the instance() synchronized. Synchronization is expensive, however, and is really only needed the first time the unique instance is created.

– Do an eager instantiation of the instance rather than a lazy instantiation.

Page 8: Design Patterns – II

Thread-Safe Singleton/* Class Singleton is an implementation of a class that only allows one instantiation.*/

public class Singleton {// The private reference to the one and only instance.// Let’s eagerly instantiate it here.private static Singleton uniqueInstance = new Singleton();

// An instance attribute.private int data = 0;

/* Returns a reference to the single instance.*/public static Singleton instance() {

return uniqueInstance;}

/*The Singleton Constructor. Note that it is private! No client can instantiate a Singleton object!*/

private Singleton() {}// Accessors and mutators here!

}

Page 9: Design Patterns – II

Singleton• What if we want to be able to subclass Singleton and have the single

instance be a subclass instance?• For example, suppose MazeFactory had subclasses

EnchantedMazeFactory and AgentMazeFactory. We want to instantiate just one factory, either an EnchantedMazeFactory or an AgentMazeFactory.

• How could we do this? Several methods:– Have the static instance() method of MazeFactory determine the particular

subclass instance to instantiate. This could be done via an argument or environment variable. The constructors of the subclasses can not be private in this case, and thus clients could instantiate other instances of the subclasses.

– Have each subclass provide a static instance() method. Now the subclass constructors can be private.

Page 10: Design Patterns – II

Method 1/* Class MazeFactory is an implementation of a class that only allows one instantiation of a subclass. */

public abstract class MazeFactory {// The private reference to the one and only instance.private static MazeFactory uniqueInstance = null;

// The MazeFactory constructor. If you have a default constructor, it can not be private here!protected MazeFactory() {}

// Return a reference to the single instance.// If instance not yet created, create "enchanted" as default.public static MazeFactory instance() {

if (uniqueInstance == null) return instance("enchanted");else return uniqueInstance;

}// Create the instance using the specified String name.public static MazeFactory instance(String name) {

if(uniqueInstance == null)if (name.equals("enchanted"))uniqueInstance = new EnchantedMazeFactory();else if (name.equals("agent"))uniqueInstance = new AgentMazeFactory();return uniqueInstance;

}}

Page 11: Design Patterns – II

Method I• Client code to create factory the first time:

MazeFactory factory = MazeFactory.instance("enchanted");• Client code to access the factory:

MazeFactory factory = MazeFactory.instance();

• Note that the constructors of EnchantedMazeFactory and AgentMazeFactory can not be private, since MazeFactory must be able to instantiate them. Thus, clients could potentially instantiate other instances of these subclasses.

Page 12: Design Patterns – II

Method I

• The instance(String) methods violates the Open-Closed Principle, since it must be modified for each new MazeFactory subclass

• We could use Java class names as the argument to the instance(String) method, yielding simpler code:

public static MazeFactory instance(String name) {if (uniqueInstance == null)uniqueInstance = Class.forName(name).newInstance();

return uniqueInstance;}

Page 13: Design Patterns – II

Method 2• Have each subclass provide a static instance method()

/*** Class MazeFactory is an implementation of a class that* only allows one instantiation of a subclass. This version* requires its subclasses to provide an implementation of* a static instance() method.*/public abstract class MazeFactory {

// The protected reference to the one and only instance.protected static MazeFactory uniqueInstance = null;

// The MazeFactory constructor.// If you have a default constructor, it can not be private here!

protected MazeFactory() {}// Return a reference to the single instance.

public static MazeFactory instance() {return uniqueInstance;}}

Page 14: Design Patterns – II

Method 2/*** Class EnchantedMazeFactory is an implementation of a class* that only allows one instantiation.*/

public class EnchantedMazeFactory extends MazeFactory {

// Return a reference to the single instance.

public static MazeFactory instance() {if(uniqueInstance == null)uniqueInstance = new EnchantedMazeFactory();return uniqueInstance;}

// Private subclass constructor!!private EnchantedMazeFactory() {}

}

Page 15: Design Patterns – II

Method 2

• Client code to create factory the first time:MazeFactory factory = EnchantedMazeFactory.instance();

• Client code to access the factory:MazeFactory factory = MazeFactory.instance();

• Note that now the constructors of the subclasses are private. Only one subclass instance can be created!

• Also note that the client can get a null reference if it invokes MazeFactory.instance() before the unique subclass instance is first created

• Finally, note that uniqueInstance is now protected!

Page 16: Design Patterns – II

Façade Pattern• Intent

– Provide a unified interface to a set of interfaces in a subsystem. Façade defines a higher-level interface that makes the subsystem easier to use.

• Motivation– Structuring a system into subsystems helps reduce complexity– Subsystems are groups of classes, or groups of classes and other

subsystems– The interface exposed by the classes in a subsystem or set of

subsystems• can become quite complex

– One way to reduce this complexity is to introduce a facade object that provides a single, simplified interface to the more general facilities of a subsystem

Page 17: Design Patterns – II

Façade

Page 18: Design Patterns – II

Façade - Applicability

• Use the Facade pattern:– To provide a simple interface to a complex

subsystem. This interface is good enough for most clients; more sophisticated clients can look beyond the facade.

– To decouple the classes of the subsystem from its clients and other subsystems, thereby promoting subsystem independence and portability

Page 19: Design Patterns – II

Façade

Page 20: Design Patterns – II

Consequences• Benefits

– It hides the implementation of the subsystem from clients, making the subsystem easier to use

– It promotes weak coupling between the subsystem and its clients. This allows you to change the classes the comprise the subsystem without affecting the clients.

– It reduces compilation dependencies in large software systems– It simplifies porting systems to other platforms, because it's less likely that

building one subsystem requires building all others– It does not prevent sophisticated clients from accessing the underlying classes– Note that Facade does not add any functionality, it just simplifies interfaces

• Liabilities– It does not prevent clients from accessing the underlying classes!

Page 21: Design Patterns – II

Example

Page 22: Design Patterns – II

Adapter Pattern• Intent

– Convert the interface of a class into another interface clients expect.– Adapter lets classes work together that couldn't otherwise because

of incompatible interfaces.• Motivation

– Sometimes a toolkit or class library can not be used because its interface is incompatible with the interface required by an application

– We can not change the library interface, since we may not have its source code

– Even if we did have the source code, we probably should not change the library for each domain-specific application

Page 23: Design Patterns – II

Adapter Pattern

Page 24: Design Patterns – II

Adapter Pattern

Page 25: Design Patterns – II

Adapter Pattern

Page 26: Design Patterns – II

The Adapter Pattern• Applicability• Use the Adapter pattern when

– You want to use an existing class, and its interface does not match the one you need• You want to create a reusable class that cooperates with unrelated classes with

incompatible interfaces

• Implementation Issues– How much adapting should be done?

• Simple interface conversion that just changes operation names and order of arguments

• Totally different set of operations– Does the adapter provide two-way transparency?

• A two-way adapter supports both the Target and the Adaptee interface. It allows an adapted object (Adapter) to appear as an Adaptee object or a Target object

Page 27: Design Patterns – II

Example 1• The classic round pegs and square pegs!• Here's the SquarePeg class:/*** The SquarePeg class.* This is the Target class.*/public class SquarePeg {

public void insert(String str) {System.out.println("SquarePeg insert(): " + str);}

}

Page 28: Design Patterns – II

Example 1

• And the RoundPeg class:/*** The RoundPeg class.* This is the Adaptee class.*/public class RoundPeg {

public void insertIntoHole(String msg) {System.out.println("RoundPeg insertIntoHole(): " + msg);

}}

Page 29: Design Patterns – II

Example 1• If a client only understands the SquarePeg interface for inserting pegs using the

insert() method, how can it insert round pegs? A peg adapter!

/*** The PegAdapter class.* This is the Adapter class.* It adapts a RoundPeg to a SquarePeg.* Its interface is that of a SquarePeg.*/public class PegAdapter extends SquarePeg {

private RoundPeg roundPeg;public PegAdapter(RoundPeg peg) {this.roundPeg = peg;}public void insert(String str) {roundPeg.insertIntoHole(str);}

}

Page 30: Design Patterns – II

Example 1// Test program for Pegs.public class TestPegs {

public static void main(String args[]) {// Create some pegs.RoundPeg roundPeg = new RoundPeg();SquarePeg squarePeg = new SquarePeg();// Do an insert using the square peg.squarePeg.insert("Inserting square peg...");// Now we'd like to do an insert using the round peg.// But this client only understands the insert()// method of pegs, not a insertIntoHole() method.// The solution: create an adapter that adapts// a square peg to a round peg!PegAdapter adapter = new PegAdapter(roundPeg);adapter.insert("Inserting round peg...");

}}• Client program output:SquarePeg insert(): Inserting square peg...RoundPeg insertIntoHole(): Inserting round peg…

Page 31: Design Patterns – II

Example 2

• Notice in Example 1 that the PegAdapter adapts a RoundPeg to a SquarePeg. The interface for PegAdapter is that of a SquarePeg.

• What if we want to have an adapter that acts as a SquarePeg or a RoundPeg? Such an adapter is called a two-way adapter.

• One way to implement two-way adapters is to use multiple inheritance, but we can't do this in Java

• But we can have our adapter class implement two different Java interfaces!

Page 32: Design Patterns – II

Example 2/***The IRoundPeg interface.*/public interface IRoundPeg {

public void insertIntoHole(String msg);}/***The ISquarePeg interface.*/

public interface ISquarePeg {public void insert(String str);

}

Page 33: Design Patterns – II

Example 2• Here are the new RoundPeg and SquarePeg classes. These are essentially the same as before

except they now implement the appropriate interface.

// The RoundPeg class.public class RoundPeg implements IRoundPeg {

public void insertIntoHole(String msg) {System.out.println("RoundPeg insertIntoHole(): " + msg);}

}

// The SquarePeg class.public class SquarePeg implements ISquarePeg {

public void insert(String str) {System.out.println("SquarePeg insert(): " + str);}

}

Page 34: Design Patterns – II

Example 2• And here is the new PegAdapter:/*** The PegAdapter class.* This is the two-way adapter class.*/public class PegAdapter implements ISquarePeg, IRoundPeg {

private RoundPeg roundPeg;private SquarePeg squarePeg;public PegAdapter(RoundPeg peg) {this.roundPeg = peg;}public PegAdapter(SquarePeg peg) {this.squarePeg = peg;}public void insert(String str) {roundPeg.insertIntoHole(str);}public void insertIntoHole(String msg){squarePeg.insert(msg);}

}

Page 35: Design Patterns – II

Example 2• A client that uses the two-way adapter:

// Test program for Pegs.public class TestPegs {

public static void main(String args[]) {// Create some pegs.RoundPeg roundPeg = new RoundPeg();SquarePeg squarePeg = new SquarePeg();// Do an insert using the square peg.squarePeg.insert("Inserting square peg...");// Create a two-way adapter and do an insert with it.ISquarePeg roundToSquare = new PegAdapter(roundPeg);roundToSquare.insert("Inserting round peg...");// Do an insert using the round peg.roundPeg.insertIntoHole("Inserting round peg...");// Create a two-way adapter and do an insert with it.IRoundPeg squareToRound = new PegAdapter(squarePeg);squareToRound.insertIntoHole("Inserting square peg...");}

}

Page 36: Design Patterns – II

Example 2

• Client program output:SquarePeg insert(): Inserting square peg...RoundPeg insertIntoHole(): Inserting round peg...RoundPeg insertIntoHole(): Inserting round peg...SquarePeg insert(): Inserting square peg...

Page 37: Design Patterns – II

Visitor Pattern• Intent

– Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.

• Motivation– Consider a compiler that parses a program and represents the parsed program

as an abstract syntax tree (AST). The AST has many different kinds of nodes, such as Assignment , Variable Reference, and Arithmetic Expression nodes.

– Operations that one would like to perform on the AST include:• Checking that all variables are defined• Checking for variables being assigned before they are used• Type checking• Code generation• Pretty printing/formatting

Page 38: Design Patterns – II

Visitor•These operations may need to treat each type of node differently•One way to do this is to define each operation in the specific node class

Page 39: Design Patterns – II

Visitor• Motivation• Problems with this approach:

– Adding new operations requires changes to all of the node classes– It can be confusing to have such a diverse set of operations in each node

class.• For example, mixing type-checking code with pretty-printing code can be hard

to understand and maintain.

• Another solution is to encapsulate a desired operation in a separate object, called a visitor. The visitor object then traverses the elements of the tree. When an tree node "accepts" the visitor, it invokes a method on the visitor that includes the node type as an argument. The visitor will then execute the operation for that node - the operation that used to be in the node class.

Page 40: Design Patterns – II

Visitor

Page 41: Design Patterns – II

Visitor

Page 42: Design Patterns – II

Visitor• Applicability• Use the Visitor pattern in any of the following situations:

– When many distinct and unrelated operations need to be performed on objects in an object structure, and you want to avoid "polluting" their

• classes with these operations– When the classes defining the object structure rarely change, but you

often want to define new operations over the structure. (If the object structure classes change often, then it's probably better to define the operations in those classes.)

• When an object structure contains many classes of objects with differing interfaces, and you want to perform operations on these objects that depend on their concrete classes

Page 43: Design Patterns – II

Visitor

Page 44: Design Patterns – II

Visitor

Page 45: Design Patterns – II

Visitor• Consequences• Benefits

– Adding new operations is easy– Related behavior isn't spread over the classes defining the object structure; it‘s

localized in a visitor. Unrelated sets of behavior are partitioned in their own visitor subclasses.

– Visitors can accumulate state as they visit each element in the object structure. Without a visitor, this state would have to be passed as extra arguments to the operations that perform the traversal.

• Liabilities– Adding new ConcreteElement classes is hard. Each new ConcreteElement gives rise to

a new abstract operation on Visitor and a corresponding implementation in every ConcreteVisitor class.

– The ConcreteElement interface must be powerful enough to let visitors do their job. You may be forced to provide public operations that access an element‘s internal state, which may compromise its encapsulation.

Page 46: Design Patterns – II

Double-Dispatch• The Visitor pattern allows you to add operations to classes

without changing them using a technique called double-dispatch• Single-Dispatch

– The actual method invoked depends on the name of the request (method signature) and the type of the receiver object

– For example, calling foo() on a object of Type X, invokes the foo() method of X

– The actual underlying type will be discovered through polymorphism– This is the standard technique used in languages like Java and C++

• Double-Dispatch– The actual method invoked depends on the name of the request and

the types of two receivers

Page 47: Design Patterns – II

Double Dispatch• For example, consider an object of type Visitor1 calling

accept(Visitor1) on an object of Type ElementA:– The Visitor1 object dispatches a call to the accept(Visitor) method of

ElementA– The accept(Visitor) method of ElementA dispatches a call back to the

visitor (Visitor1), invoking the visit(ElementA) method of Visitor1 and passing itself as an argument.

– This round trip effectively picks up the right type of Element, ensuring that the correct visit() method of the Visitor object is called

• Effectively, then, the method invoked depends on the request name (accept(Visitor)), the type of the Element object (ElementA) and the type of the Visitor object (Visitor1)

Page 48: Design Patterns – II

The Composite 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.

• Motivation

Page 49: Design Patterns – II

Motivation

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

Page 50: Design Patterns – II

Composite Pattern

Page 51: Design Patterns – II

The Composite Pattern

• Consequences• Benefits– It makes it easy to add new kinds of components– It makes clients simpler, since they do not have to

know if they are dealing with a leaf or a composite component

• Liabilities– It makes it harder to restrict the type of

components of a composite

Page 52: Design Patterns – II

The Composite Pattern• Implementation Issues• A composite object knows its contained components, that is, its children.

Should components maintain a reference to their parent component?– Depends on application, but having these references supports the Chain of

Responsibility pattern• Where should the child management methods (add(), remove(),

getChild()) be declared?– 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.

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

Page 53: Design Patterns – II

Composite Pattern

Page 54: Design Patterns – II

Composite Pattern• Implementation Issues• 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• Is child ordering important?

– Depends on application• Who should delete components?

– Not a problem in Java! The garbage collector will come to the rescue!• What's the best data structure to store components?

– Depends on application

Page 55: Design Patterns – II