design patternshaase/lehre/patterns/... · use the observer pattern when ‣the abstraction has two...
TRANSCRIPT
Oliver Haase
Design PatternsObserver
1
Description
2
‣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
Model
View 1
View 2
View 3
Observer
Subject,Observable
notifyget update
Motivating Example
3
‣ keep consistency between decoupled objects
‣ often used in the context of the MVC architectural pattern, more general, related to event listeners
‣ helps build a layered architecture
update()Observer
concreteSubject.get()
attach(Observer)detach(Observer)notify()
Subject
update()ConcreteObserver
observers
get()set(s)
stateConcreteSubject concreteSubject
state = snotify()
return state
for each o in observers o.update()
General Structure
4
‣knows its observers‣provides interface for attaching and detaching observers
‣concrete observed subject‣maintains state ‣notifies observers about change of state
defines call-back operation to get notified
uses concrete subject’s get
operations to pull updated state
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
8
Pull Model Push Model
signature of notify operation
number of operation calls
unnecessarily exchanged state
information
independent of subject’s state fields
subject to modification if subject’s state fields are changed
higher lower
fewer more
Observer in Java
9
Call of notifyObservers without prior call of setChanged has no effect!
update(Observable, Object)
<<interface>>Observer+ addObserver(Observer)
+ deleteObserver(Observer)+ notifyObservers()# setChanged()
Observable
update(Observable, Object)MyObserver
observers
getState()setState(s)
stateMyObservable
state = ssetChanged()notifyObservers()
Observer in Java‣ Observable is a class.
‣What if our observable subject already has a super-class?
‣What aspects of inheritance are needed?
10
→ implementation inheritance, and
→ interface inheritance
⇒ SmartAdapter!
‣ Can class Observable be delegated to?
→ No, because setChanged is protected.
‣ Adapter & Delegation (see Implementation Reuse patterns)
Observer in Java: SmartAdapter
11
update(Observable, Object)
<<interface>>Observer+ addObserver(Observer)
+ deleteObserver(Observer)+ notifyObservers()# setChanged()
Observable
update(Observable, Object)MyObserver
observers
getState()setState(s)
statedelegate
MyObservable
MySuper
state = sdelegate.setChanged()delegate.notify()
+ setChanged()+ getState()+ setState(s)
delegateSmartObservableAdapter
delegate.setState(s)
return delegate.getState()
super.setChanged()
Observer and ConcurrencyNaive implementation of attach(), detach(), and notify()
12
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();}
}}
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!
14
→ 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!
Relationship with other Patterns
15
MVC architectural pattern almost always uses Observer pattern.
Mediator
16
Motivation
17
‣ 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.
Description
18
‣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
Mediator Colleague
ConcreteMediator ConcreteColleague2ConcreteColleague1
mediator
General Structure
19
‣Each colleague-class knows its broker-class‣Each colleague-object works with its broker and not with the other colleague-objects
‣implements the general behavior by coordinating the colleague-objects‣knows and manages its colleague-objects
defines an interface for the interaction with
colleague-objects
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
24
‣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
Description
25
‣ 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.
return new Memento(state)
CareTakergetState()setState(state)
stateMemento
createMemento()setMemento(Memento m)
stateOriginator
memento
state = m.getState()
General Structure
26
‣Creates a memento object capturing the originator’s internal state.‣Use the memento object to restore its previous state.
The memento is opaque to the caretaker, and the caretaker
must not operate on it.
‣Saves the state information‣Full access only from the originator
Interactions
27
:Caretaker :Originator aMemento:Memento
createMemento() new Memento()
setState()
setMemento(aMemento) getState()
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?
29
→ Make Memento an inner class of Originator
Ensure encapsulation in Java
30
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 int getState() {return state;
}}
}
private method of static inner class can be called by outer class
Relationship with other Patterns
‣ Command objects can use mementos to maintain state for undoable operations.
‣ Iterator can use memento objects to save state of iteration.
31
Strategy
32
Description
33
‣Object based behavioral pattern
‣ Purpose: Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independent of clients that use it.
‣Also Known As: Policy
Motivating Example
34
@Immutablepublic final class OriginalAIOpponent { private final Level skill;
public OriginalAIOpponent(Level skill) { this.skill = skill; } public void repelMove(int posX, int posY) { switch (skill) { case LOUSY: // do something break; case ... } } public void printCoolSlogan() { switch (skill) { case LOUSY: // print something break; case ... } }}
public enum Level { LOUSY, STRONG;}
Motivating Example
‣Options if we need to add a new skill level?• add case to Opponent class (easy to forget an occurrence)
• create subclass of Opponent• outsource different behavior
35
Motivating ExampleDefine strategy interfaces:
36
public interface RepelStrategy { void repelMove(int posX, int posY);}
public interface SloganStrategy { void printCoolSlogan();}
Sample implementation:public class LousySloganStrategy implements SloganStrategy { @Override public void printCoolSlogan() { System.out.println("Please don't hurt me!"); }}
Motivating Example
37
public class AIOpponent { private final RepelStrategy repelStrategy; private final SloganStrategy sloganStrategy;
public AIOpponent(RepelStrategy repelStrategy, SloganStrategy sloganStrategy) {
this.repelStrategy = repelStrategy; this.sloganStrategy = sloganStrategy; }
public void repelMove(int posX, int posY) { repelStrategy.repelMove(posX, posY); } public void printCoolSlogan() { sloganStrategy.printCoolSlogan(); }}
AIOpponent o = new AIOpponent(new LousyRepelStrategy(), new LousySloganStrategy());
Sample Structure
38
printCoolSlogan()
<<interface>>SloganStrategy
sloganStrategy.printCoolSlogan()
printCoolSlogan()LousySloganStrategy
ContextInterface()AIOpponent
printCoolSlogan()StrongSloganStrategy
sloganStrategy
AlgorithmInterface()Strategy
strategy.AlgorithmInterface()
AlgorithmInterface()ConcreteStrategyA
ContextInterface()Context
AlgorithmInterface()ConcreteStrategyB
strategy
General Structure
39
‣gets configured with ConcreteStrategy object‣may define interface that lets Strategy access its data
implements the algorithm using the Strategy interface
interface common to all supported algorithms
Applicability
Use the Strategy pattern when
‣ many related classes differ only in their behavior. Strategies provide a way to configure a class with one of many behaviors.
‣ need of different variants of algorithms.
‣ an algorithm uses data that clients shouldn’t know about
‣ A class defines many behaviors (use of multiple conditional statements).
‣ Remark: be aware of switch-case-statements, they have a smell
40
Interactions
‣ A context forwards requests from its clients to its strategy.
‣ Data handover:• A context may hand over all data required by the algorithm
to the strategy method.
• The context can pass itself as an argument to Strategy method.
41
Pros & Cons‣ Pro:
• Re-usability of algorithms
• An alternative to sub-classing of the context class
• Choice of different implementations
• Elimination of conditional statements
• clean separation of different algorithms ➔ easier to test
‣ Con:• Clients must be aware of different strategies
• Communication overhead
• Increased number of objects
42
Real World Examples‣ ThreadPoolExecutor gets RejectedExecutionHandler
object to decide strategy for tasks that cannot be executed (AbortPolicy, CallerRunsPolicy, DiscardOldestPolicy, DiscardPolicy)
‣ Java Comparator interface
‣ Java AWT and Swing, e.g. paintBorder() method of JComponent
43
Relationship with other Patterns
‣ Flyweight can be used to implement strategy objects.
44