Presentation is loading. Please wait.

Presentation is loading. Please wait.

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.

Similar presentations


Presentation on theme: "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."— Presentation transcript:

1 Design Patterns – II Lecture IV

2 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

3 Structure Controlled access to sole instance Permits a variable number of instances

4 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! }

5 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()); }

6 Singleton Pattern And the test program output: First reference: Singleton@1cc810 Singleton data value is: 34 Second reference: Singleton@1cc810 Singleton data value is: 34

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

8 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! }

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

10 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; }

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

12 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; }

13 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;} }

14 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() {} }

15 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!

16 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

17 Façade

18 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

19 Façade

20 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!

21 Example

22 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

23 Adapter Pattern

24

25

26 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

27 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); }

28 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); }

29 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);} }

30 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…

31 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!

32 Example 2 /** *The IRoundPeg interface. */ public interface IRoundPeg { public void insertIntoHole(String msg); } /** *The ISquarePeg interface. */ public interface ISquarePeg { public void insert(String str); }

33 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); }

34 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);} }

35 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..."); }

36 Example 2 Client program output: SquarePeg insert(): Inserting square peg... RoundPeg insertIntoHole(): Inserting round peg... SquarePeg insert(): Inserting square peg...

37 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

38 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

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

40 Visitor

41

42 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

43 Visitor

44

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

46 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

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

48 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

49 Applicability Use 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.

50 Composite Pattern

51 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

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

53 Composite Pattern

54 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

55


Download ppt "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."

Similar presentations


Ads by Google