Design Patterns Observer Oliver Haase 1
Description ‣ Object based behavioral pattern ‣ Purpose : Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically. ‣ Also Known As : Publish-Subscribe 2
Motivating Example ‣ keep consistency between decoupled objects ‣ often used in the context of the MVC architectural pattern, more general, related to event View 1 listeners ‣ helps build a layered architecture Model View 2 Subject, Observable View 3 notify get update Observer 3
General Structure ‣ knows its observers defines call-back operation ‣ provides interface for attaching to get notified and detaching observers observers Subject Observer uses concrete attach(Observer) update() detach(Observer) subject’s get notify() for each o in observers operations to pull o.update() updated state concreteSubject ConcreteSubject ConcreteObserver state update() get() set(s) concreteSubject.get() return state state = s notify() ‣ concrete observed subject ‣ maintains state ‣ notifies observers about change of state 4
Applicability Use the Observer pattern when ‣ the abstraction has two aspects where one depends on the other. Encapsulating these aspects in separate objects will increase the chance to reuse them independently. ‣ the subject doesn't know in advance how many observer objects it will have. ‣ the subject should be able to notify its observer objects without knowing them. 5
Interactions ‣ ConcreteSubject notifies its observers whenever a change to its internal state happens. ‣ After a ConcreteObserver gets notified, it may query the subject state by using the get() method. ConcreteObserver uses this information to change its own internal state. 6
Pros & Cons ‣ Pros: • Decoupling of subject and observer, each can be extended and reused individually. • Dynamic addition and removal of observers at runtime. • Subject broadcasts notification automatically to all interested objects, no matter how many or which kind of observers are registered. ‣ Cons: • May result in many notifications the observers are not interested in • Potentially difficult for the observers to figure out the specific state change of the subject. 7
Push vs. Pull Model ‣ In its purest form, notification does not carry updated state information → Pull Model ‣ Variant: notification contains updated state → Push Model Pull Model Push Model signature of notify independent of subject’s state subject to modification if subject’s operation fields state fields are changed number of higher lower operation calls unnecessarily exchanged state fewer more information 8
Observer in Java observers <<interface>> Observable Observer + addObserver(Observer) update(Observable, Object) + deleteObserver(Observer) + notifyObservers() # setChanged() MyObservable MyObserver state update(Observable, Object) getState() setState(s) state = s setChanged() notifyObservers() Call of notifyObservers without prior call of setChanged has no effect! 9
Observer in Java ‣ Observable is a class. ‣ What if our observable subject already has a super-class? ‣ What aspects of inheritance are needed? → implementation inheritance, and → interface inheritance ‣ Adapter & Delegation (see Implementation Reuse patterns) ‣ Can class Observable be delegated to? → No, because setChanged is protected. ⇒ SmartAdapter! 10
Observer in Java: SmartAdapter observers <<interface>> Observable Observer + addObserver(Observer) update(Observable, Object) + deleteObserver(Observer) + notifyObservers() # setChanged() MySuper MyObserver update(Observable, Object) MyObservable SmartObservableAdapter state delegate delegate + setChanged() super.setChanged() getState() + getState() setState(s) + setState(s) return delegate.getState() state = s delegate.setState(s) delegate.setChanged() delegate.notify() 11
Observer and Concurrency Naive implementation of attach() , detach(), and notify() public final class NaiveSubject { private final Vector<Observer> observers; ... public final void attach(Observer o) { observers.addElement(o); } public final void detach(Observer o) { observers.removeElement(o); } public final void notifyObservers() { for(Observer o : observers) { o.update(); } } } 12
Observer and Concurrency ‣ Vector is thread-safe, but NaiveSubject is not. ‣ NaiveSubject will throw ConcurrentModificationException if a thread adds or removes an observer while another notifies the observers. ‣ Possible solutions: • Java Monitor Pattern (risky because of alien method call) • Use CopyOnWriteArrayList • Copy vector before iteration (this is how JDK Observable does it) 13
Observer and Concurrency ‣ Do not attach observer to subject inside observer’s constructor! → Otherwise the observer’s this reference escapes before the subject is fully constructed. Reminder: In general, do not register a listener at an event source within listener’s constructor! 14
Relationship with other Patterns MVC architectural pattern almost always uses Observer pattern. 15
Mediator 16
Motivation ‣ Complex systems often require that the participating objects know each other. ‣ This might end up in a nontransparent situation which is hard to understand. ‣ The Mediator now supervises this communication. ‣ It knows all the objects taking part and the objects are only aware of the mediator. 17
Description ‣ Object based behavioral pattern ‣ Purpose : Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently. ‣ Also Known As : Broker 18
General Structure defines an interface for the interaction with colleague-objects mediator Mediator Colleague ConcreteMediator ConcreteColleague1 ConcreteColleague2 ‣ implements the general behavior by coordinating the ‣ Each colleague-class knows its broker-class colleague-objects ‣ Each colleague-object works with its broker ‣ knows and manages its and not with the other colleague-objects colleague-objects 19
Applicability Use the Mediator pattern when ‣ there are many objects that have to work with each other, but the dependencies are unstructured and hard to understand. ‣ reusability of object is hard, because it has dependencies to many other objects. ‣ a behavior that's distributed between several classes should be customizable without a lot of subclassing 20
Pros & Cons ‣ Pro: • Limits subclassing • Decoupled colleague-objects • n:n-relations converted to 1:n-relations • Abstracts how the colleague-objects work with each other • Centralized control ‣ Con: • Broker class might turn into a hard to maintain monolith 21
Relationship with other Patterns ‣ Facade: Subsystem-classes don‘t know about the facade; colleague-classes know their broker. ‣ Colleague objects can notify the broker using the Observer pattern. ‣ If the Mediator alters message then it is an Adapter pattern. 22
Memento 23
Description ‣ Object based behavioral pattern ‣ Purpose : Capture the internal state of an object without violating encapsulation and thus providing a mean for restoring the object into initial state when needed. ‣ Also Known As: Token 24
Description ‣ Used for: Undo functionality ‣ A Memento is an object which saves a snapshot of an originator ’s internal state. ‣ Only the originator has access to the state of the memento object. 25
General Structure The memento is opaque to ‣ Saves the state information the caretaker, and the caretaker ‣ Full access only from the originator must not operate on it. memento Memento CareTaker state getState() setState(state) ‣ Creates a memento object Originator capturing the originator’s internal state state. return new Memento(state) createMemento() ‣ Use the memento object to restore setMemento(Memento m) its previous state. state = m.getState() 26
Interactions :Caretaker :Originator aMemento:Memento createMemento() new Memento() setState() setMemento(aMemento) getState() 27
Applicability Use the Memento pattern if ‣ an object's state must be captured so that it can be restored later on, and if ‣ explicitly passing the state of the object would violate encapsulation. 28
Consequences ‣ Preserves encapsulation. ‣ CareTaker simplifies originator code. ‣ Using the memento pattern can be expensive. ‣ In some languages it is hard to ensure that only the Originator can access the Memento ’s state. • How can we ensure this in Java? → Make Memento an inner class of Originator 29
Ensure encapsulation in Java public final class Originator { private int state; public Memento createMemento() { return new Memento(state); } public void setMemento(Memento memento) { state = memento.getState(); } public static final class Memento { private final int state; private Memento(int state) { this.state = state; private method of static inner class } can be called by outer class private int getState() { return state; } } } 30
Recommend
More recommend