CS 4321 – Ch 6, Sections 6.5-6.9 Using Design Patterns Read Sections 6.5-6.9, pages 231-238. These notes are mostly original except for some of the examples. Section 6.5 – The Singleton Pattern 1. Sometimes it is important to have only one instance of a class. Examples: window manager, file system, print spooler, logging, caching, thread pool, data access object. 2. This problem is solved by the singleton pattern. This is the way the pattern is described: a. Context – You have a class where only one instance should exist. b. Problem – How do you ensure that it is not possible to create more than one instance? c. Forces – Use of a public constructor can’t guarantee that no more than one instance will be created. The singleton instance must be accessible to all classes that require it. d. Solution – 3. Example 1 – The JVM uses classes that are loaded automatically to draw GUI elements. java.awt.Toolkit is an abstract class that binds the various AWT components to particular native toolkit implementations. The Toolkit class has a Toolkit.getDefaultToolkit() factory method that returns the platform-specific subclass of Toolkit. The Toolkit object is a singleton because the AWT needs only a single object to perform the binding and the object is relatively expensive to create. The toolkit 1 methods must be implemented in an object and not as static methods of a class because the specific implementation is not known by the platform-independent components. The name of the specific Toolkit subclass used is specified by the "awt.toolkit" environment property accessed through System.getProperties(). The binding performed by the toolkit allows, for example, the backing implementation of a java.awt.Window to bind to the platform-specific java.awt.peer.WindowPeer implementation. Neither the Window class nor the application using the window needs to be aware of which platform-specific subclass of the peer is used. Source: http://en.wikipedia.org/wiki/Singleton_pattern 4. Example 2 The classic singleton exhibits lazy instantiation. public class Company { private static Company theCompany = null; private Company() { } public static Company getCompany() { if(theCompany == null) { theCompany = new Company(); } return theCompany; } public void doCompanyBusiness() { } } and we use code like this to use the Singleton: Company company = Company.getCompany(); company.doCompanyBusiness(); 5. Example 3 – A data access object (DAO) might be designed to be a singleton for any number of reasons, for instance, logging of access activity. Section 6.5 – Homework Problems 1. You want a timer object that can be used for performance analysis. The timer can time many events, either sequentially or simultaneously and can be used anywhere in the code. It is desired for this to be simple to use for the person doing the performance analysis. They would like to be able to add, start, and stop timers with ease. At the conclusion of the program, the timer should have a simple feature to dump all the times contained in the timer. Model this situation with UML and illustrate any design patterns you use. 2 Section 6.6 – The Observer Pattern 1. Example 1 – Suppose we want a class, WeatherStation that represents a real-time data collection location for weather related data. We also want other classes to be able to “observe” the WeatherStation. For instance, we might have a ValdostaTVObserver and a TiftonTVObserver and others. When the weather changes at the WeatherStation, we want the “observers” to be notified. One solution is to hardcode the WeatherStation with references to the two observers; however, this is not very flexible. What happens when we want to add a new observer? What happens when an observer goes offline temporarily? This strong dependency between the WeatherStation and the observers is a problem! 2. Context – You have a one-to-many relationship such that when the one object changes the dependents are notified of the change. 3. Problem – How should the communication take place so that the classes don’t become inseperable? 4. Forces – You desire flexibility so that the dependent objects can be changed dynamically. 5. Solution – In other words, we provide a class, Observable whose job is to manage the objects that are observing it. It provides methods to add and remove observers and to notify all the observers. The Observable maintains a list of observers, but notice that it doesn’t need to know any concrete classes; it simply knows that each observer is an Observer. When notifyObservers is called, it loops through the list of Observers and calls update (the only thing it knows about the observers) on each one. Thus, each ConcreteObserver can take whatever action it wants in response to the update. Also notice that we have removed all dependencies between the ConcreteObservable and the ConcreteObservers. 6. To facilitate the use of the Observer pattern, Java provides the Observable class and the Observer interface. 3 7. An implementation of several classes in Observable is shown below. Java’s implementation is more complex as it is thread-safe. public class Observable { private ArrayList<Observer> observers = new Observable<>(); private hasChanged = false; public void addObserver( Observer o ) { observers.add(o); } public void notifyObservers( Object obj ) { if( hasChanged ) for( Observer o : observers ) o.update( this, obj ); } protected void setChanged( Boolean hasChanged ) { this.hasChanged = hasChanged; } } 8. Example 1 continued: 9. The code for a simple example: import java.util.*; public class Driver1 { public static void main( String[] args ) { WeatherStation weatherStation = new WeatherStation(); weatherStation.addObserver( new ValdostaTV() ); weatherStation.addObserver( new TiftonTV() ); weatherStation.setTemp(99); } } 4 class WeatherStation extends Observable { private int temp; public void setTemp( int val ) { temp = val; setChanged(); notifyObservers(temp); } } class ValdostaTV implements Observer { public void update( Observable observable, Object arg ) { System.out.println( "ValdostaTV brings you the temp: " + arg ); } } class TiftonTV implements Observer { public void update( Observable observable, Object arg ) { System.out.println( "TiftonTV brings you the temp: " + arg ); } } 10. Example 2 – Implementation of Observer in Java Section 6.6 – Homework Problems 1. Write code for an Observable class that has these methods: addObserver(o:Observer), removeObserver(o:Observer), notifyObservers(arg:Object). 2. What dependency relationship (coupling) has been removed by using the observer design pattern? Briefly describe. 3. An Inventory class has an inventoryLevel property. When the inventoryLevel is changed, the new value is sent to all observers. (a) Write the code to implement this example and provide a small example (main) using the Observer pattern (Java implementation). (b) Model this situation with UML and illustrate any design patterns you use. 4. Briefly describe 3 examples of where the Observer design pattern could be applied. The examples should be useful and practical and not one of the ones mentioned in class, notes, nor text. 5 Section 6.7 – The Delegation Pattern 1. The Delegation pattern is not generally considered a pattern, it is an idiom or principle. a. b. c. d. Context – You are designing a class and you need a method that is already implemented in another class. Problem – How do you most effectively make use of a method in another class? Forces – Inheritance is not appropriate because you don't want to reuse all the methods in the other class. Solution – use composition and delegate to the class with the method needed. 2. Example 1 – Suppose you need to create a custom Stack class and subclassing Java’s Stack class is not suitable. 3. Example 2 - Consider the Airline problem from Chapter 5: 4. The Delegation pattern brings together three principles that encourage flexible design: a. favoring association instead of inheritance when the full power of inheritance is not needed; b. avoiding duplication of chunks of code; and c. accessing ‘nearby’ information only. Violation of any of these principles should be avoided, as explained below. Design Principle: (Law of Demeter) "Only talk to your neighbors." A class shouldn't have to know much about distant classes. A method/class should have limited knowledge of an object model. 6 Section 6.7 – Homework Problems 1. A Seminar has a name, number, and summary. Each Seminar has many Offerings, where each Offering has a date. Each Offering has many Attendees, where each attendee has a name. Frequently, the name of the seminar is need when the Attendee object is possessed. Model this situation with UML and illustrate any design patterns you use. Section 6.8 – The Adapter Pattern 1. The Adapter pattern: a. b. c. d. Context – You are designing a class hierarchy and want to reuse a class from another hierarchy. Problem – How do you provide an API for the new hierarchy that is independent of the reuse class? Forces – Multiple inheritance is not appropriate. Solution – Design the new hierarch. Where an existing class could be used in the new hierarchy, compose that new class with the reuse class and delegate as needed. 2. Definitions a. b. c. d. Allows objects with different interfaces to communicate with one another. Convert the interface of one class to the interface that a client expects. Wrap an existing class with a new interface. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces. 3. The difference between Delegation and Adapter is that (a) Delegation is concerned with reusing a method and Adapter is concerned with reusing a class, (b) Adapter uses Delegation. 4. When doing design, the Adapter Pattern frees us from having to think about the interface for existing classes. 5. Facades and Adapters are both wrappers, but they are different types of wrappers. A Facade simplifies an interface while an Adapter converts one interface into another. 6. There are many Adapters in java: WindowAdapter, MouseAdapter, KeyboardAdaptor, InputStreamReader. A WindowAdapter class is a convenience class that wraps the WindowListener interface. A window in Java can respond to a number of events such as: Activated, Closed, LostFocus, Iconified, etc. If you implement the WindowListener interface, you must implement all these methods. Instead, you can use the WindowAdapter class which implements the WindowListener class with null implementations. Thus, as a convenience, you can just extend the WindowAdapter class and override just the methods you need. 7 Section 6.8 – Homework Problems 1. Write an adapter to convert a LinkedList into a FIFO queue with methods add, getNext, and size. 2. When using the Adapter pattern, what do you do if the adaptee class doesn't do all the things you need? Section 6.9 – The Facade Pattern 1. The goal of the Façade pattern is to simplify access to a class, group of related class, or a package. A nonsoftware example of the Facade pattern: a. Context – A component contains several complex packages. The client that uses the component must work findwith many different classes in the package. b. Problem – How do you provide a simplified API that clients can use. c. Forces – Modifications made to the classes necessitates a review of all client code that utilizes the changed classes. d. Solution: Create a class called the Facade which will simplify the use of the component (package). It contains a set of public methods so that the client can rely solely on the Facade and not have to access the other classes in the package. Before Facade After Facade 8 2. Example – In the airline example making a booking involves several steps: creating the Booking instance, linking it to the PassengerRole and the SpecificFlight, linking the SpecificFlight to the Booking, and linking the PassengerRole to the Booking. A façade could make it easier for a client to accomplish this by making all the linkages. 9