OBSERVER DESIGN PATTERN “Studying software design patterns is an effective way to learn from the experience of others” REFERENCE SITES: 1 of 12 EXAMPLE CONTEXT Application Different ways of presenting the results. Presentation of results Coupling Computation of results “If the presentation classes and computation classes are coupled, then can’t reuse one without dragging along the other.” Application runs algorithms on data, and produces the results: a, b, and c. How should the results be communicated to the interested parties? 2 of 12 THE OBSERVER PATTERN DESCRIPTION Context: – When associations are created between two (groups of) classes, the code for the classes becomes inseparable (coupled): – Compiling one requires compiling the other. – Reusing one requires reusing the other. – Changing one likely requires changing the other. Consequences of coupling. Issues: – How do you reduce the interconnection between classes, especially between classes that belong to different modules or subsystems? – Removing interconnection implies removing association. – When interconnection is removed, how can the classes communicate? – You want to minimize the coupling, but maintain usability. 3 of 12 THE OBSERVER PATTERN THE PROBLEM – BALANCING OF FORCES (TRADEOFF) DataSupplier DataUser In order to design the DataSupplier and DataUser for reusability – seperately and independently – need to remove the association, but maintain usability. 4 of 12 INTERMEDIATE SOLUTION (ANTI‐PATTERN) «Observable» addObserver notifyObservers * «ConcreteObservable» «interface» * «Observer» update Level 2 Level 1 «ConcreteObserver» The user application has two layers of software, Level 2 and Level 1. The application adds observers by invoking addObserver in Level 2 for each observer in Level 1 that is interested in the data of the observerable in Level 1. Whenever a Level 1 observable’s data has changed, the observable invokes notifyObservers in Level 2. The Level 2 method notifyObservers invokes the update method of each observer that is registered to receive data updates from the observable. 5 of 12 INTERMEDIATE SOLUTION (ANTI‐PATTERN) «Observable» addObserver notifyObservers * «ConcreteObservable» «interface» * «Observer» update Level 2 Level 1 «ConcreteObserver» The components ConcreteObservable and ConcreteObserver may be reused independently, since there is no direct association between them: – In one application, many different Level 1 implementations may exist, and only one level 2, is required. For example: – A WeatherForecaster and many different kinds of WeatherViewers, and – Mouse events and many different watchers of mouse events. – Keyboard events and many different watchers of keyboard events. – However, reusability of a component requires the software in which it is reused has Level 2. – The problem is that this requires sharing the Level 2 implementation. 6 of 12 THE OBSERVER PATTERN THE SOLUTION «Observable» addObserver notifyObservers * «ConcreteObservable» «interface» * «Observer» update Compiler Layer Application Layer «ConcreteObserver» Level 2 is implemented by the compiler: – For example, Java provides Observable and Observer classes: – i.e., Java implements the Observer Pattern. – This is a better solution than the previous anti‐pattern, but note that it requires the reusable components use the Java compiler. – This is reasonable, since the components are written in Java. 7 of 12 EXAMPLE: OBSERVABLE KEYBOARD «Observable» addObserver notifyObservers setChanged Keyboard * «interface» * «Observer» update MyApp Java Compiler Layer myApp Layer KBHandler update «interface» Runnable • MyApp registers KBHandler as an observer of the observable Keyboard. • The observable Keyboard has a thread that continuously waits for and receives input from the Standard In (user input). • When it receives user input, the thread calls setChanged and notifyObservers. • Java’s implementation of the Observable invokes the update method of the KBHandler, which prints the message to Standard Out. 8 of 12 Keyboard CLASS (OBSERVABLE) import java.util.Observable; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; public class Keyboard extends Observable implements Runnable { public void run() { try { final InputStreamReader isr = new InputStreamReader(System.in); final BufferedReader br = new BufferedReader(isr); while (true) { String response = br.readLine(); setChanged(); notifyObservers(response); } } catch (IOException e) { e.printStackTrace(); } } } 9 of 12 KBHandler CLASS (OBSERVER) import java.util.Observable; import java.util.Observer; /* this is Event Handler */ public class KBHandler implements Observer { private String userInput; public void update(Observable obj, Object arg) { if (arg instanceof String) { userInput = (String) arg; System.out.println("\nReceived Response: " + userInput); } } } 10 of 12 myApp CLASS (main METHOD) public class MyApp { public static void main(String[] args) { System.out.println("Enter your message, please >"); // create an observable source – thread reads from stdin final Keyboard myKBEventSource = new Keyboard(); // create an observer final KBHandler myKBHandler= new KBHandler(); // Register the observer to the observable myKBEventSource.addObserver(myKBHandler); // starts the event thread Thread thread = new Thread(myKBEventSource); thread.start(); } } 11 of 12 REUSABILITY ANALYSIS • Keyboard class can be reused independently • That is, the KBHandler class is not required, since Keyboard has no association to KBHandler. • In fact, Keyboard does not even know that KBHandler exists! • KBHandler class can be reused independently • That is, the Keyboard class is not required, since KBHandler has no association to Keyboard. • In fact, KBHandler does not even know that Keyboard exists! • MyApp class cannot be reused independently, since it has associations to Keyboard and KBHandler. • However, MyApp shows how to implement Observables and Observers using Java’s Observer Design Pattern. 12 of 12
© Copyright 2024