Description
The Observer Pattern
is one the fundamental design patterns described by the Gang
Of Four (GoF) in the volume “Design
patterns – Elements of reusable object-oriented software”.
It is a behavioural
pattern used when there is a “one to
many” dependency between different objects: when the observed
object (called Subject in the pattern) changes
its state, all objects depending on it
(called Observers) must be notified and
updated.
Implementation
The code of the following example can be found here: Design Patterns in Java Github Repository.Subject
public class Subject {
private List observers = new ArrayList();
private int state;
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
notifyAllObservers();
}
public void attach(Observer observer){
observers.add(observer);
}
public void notifyAllObservers(){
for (Observer observer : observers) {
observer.update();
}
}
}
Subject is the
object from which other objects depend
(Observers). The class Subject keeps an internal
list of Observers (see below) that can
be added with the public method “attach(Observer observer)” (the
class could also implement a “detach” method for removing
Observers that are not used anymore).
The variable “state”
represents the internal state
of this object (in our simplified example it’s an integer), with
public setter and getter methods.
When the state of
the Subject varies, through the setState method, we have to iterate
over the list of Observers and notify
the change to each of them. The
notification is sent to all attached Observers: it’s up to the
Observer to handle or ignore the notification.
As you can see from
the example above it’s very important that notifyAllObservers() is
called only after the state of the Subject is updated, and not before
(the Subject state must be
self-consistent), because all Observers
will be updated as a result of the notification. This is especially
true if you need to subclass the Subject class. A possible solution
is to define a template method
with abstract operations that the subclasses can override, and call
notifyAllObservers() as the last instruction of the method.
Notice that the
class Subject keeps strong references
to the attached Observers. This can cause memory
leaks if some Observers are not used
anymore: to fix this problem you can create a Subject class keeping
weak references
to the Observers, instead of strong ones.
Abstract Observer
public abstract class Observer {
protected Subject subject;
public abstract void update();
}
Observer is an
abstract class
that must be extended. It keeps a reference
to the Subject (to be able to be
connected to it through the “attach” method and to get/set its
state through the public setter/getter methods); it also exposes an
abstract “update” method that gets invoked when
the internal state of the Subject varies
(this is the way the Subject communicates its changes to the
Observers).
Concrete Observer
public class BinaryObserver extends Observer{
private Subject subject;
public BinaryObserver(Subject subject){
this.subject = subject;
this.subject.attach(this);
}
@Override
public void update() {
System.out.println( "Binary String: " + Integer.toBinaryString( subject.getState() ) );
}
}
There are two things
to notice here:
- the ConcreteObserver “BinaryObserver” (which prints the number representing the state of the Subject in binary notation) keeps a reference to the Subject: this reference is used to connect this ConcreteObserver to the Subject (“attach” method of the Subject) and to retrieve the Subject’s state through the “getState” method;
- the class exposes an “update” method used to receive the notification sent by the Subject when its state varies.
The ConcreteObserver
can also directly edit the state of the Subject via the “setState”
method. In this case the ConcreteObserver that made the change gets
also notified by the Subject (the method “nofityAllObservers”
iterates over all attached Observers) and, after receiving the
notification, it may possibly change its internal state.
public class OctalObserver extends Observer{
private Subject subject;
public OctalObserver(Subject subject){
this.subject = subject;
this.subject.attach(this);
}
@Override
public void update() {
System.out.println( "Octal String: " + Integer.toOctalString( subject.getState() ) );
}
}
This is another
ConcreteObserver, printing the variable “state” of the Subject in
octal notation.
Client
public class ObserverPatternDemo {
public static void main(String[] args) {
Subject subject = new Subject();
new OctalObserver(subject);
new BinaryObserver(subject);
System.out.println("First state change: 15");
subject.setState(15);
System.out.println("Second state change: 10");
subject.setState(10);
}
}
![]() |
Sequence |
Insights
Advantages
The Observer pattern enables you to decouple the Subject and the Observers, making them reusable and modifiable independently; it also enables you to add or remove Observers according to your specific needs. Infact the Subject only knows to have some Observers attached to it and which method it has to invoke to notify its state changes (interface for notifications), but it doesn’t know more: what kind of classes they are, what they do, etc. (the coupling is abstract and minimal). If the Observers were tightly coupled to the Subject this would not be possible.
Being weakly
coupled, the Subject and the Observers could belong to different
layers of abstraction in a system: for
example the Subject could lie on a lower level and the Observers in a
higher level, thus preserving the layering of the system.
Observers depending on different Subjects
A very easy way to
handle this situation consists of passing
the Subject itself as a parameter in
the “update” method, thus allowing the Observer to understand
from which Subject the notification is coming.
If we have different
interdependent Subjects and an Observer attached to them, the
Observer pattern, as previously described, could be inefficient,
because the Observer could receive multiple sequential notifications
from the different Subjects as their states change. A better solution
would be to have a single notification issued when all Subjects have
changed. Solution: ChangeManager.
Notify called from the Client
With the standard
Observer pattern, where the Subject invokes “update” on each
Observer to notify its state changes (see the method “setState”),
inefficiencies may arise when there are multiple
sequential modifications on the
Subject: infact each modification is notified to each Observer
separetely.
In a different and
better implementation the Client changes the Subject’s state and is
responsible for notifying all Observer only after the different
sequential modifications have been made. However if the Client
forgets to issue the notifications errors may arise.
“Update” method with or without parameters (push or pull model)
- Without parameters (see the example above): the Subject simply informs all Observers that a change has occurred, but it’s the Observer’s responsibility to understand exactly what happened (pull method); this implementation decouples Subject and Observers making them easily reusable, but the notification mechanism may become inefficient.
- With parameters: the Subject gives the Observers detailed information about the change that has occurred (push method): the notification management is more efficient, but Subject and Observers become more tightly coupled and therefore less easily reusable.
Specifying modifications of interest
Let’s suppose that
an Observer is interested only in changes of the Subject of some
kind, and not all. A very easy solution is to specify in the “attach”
method of the Subject the type of
modification of interest:
public void
attach(Observer observer, Interests interest), where interest is the
type of modification of interest
We also have to change the
“update” method of the AbstractObserver to specify what kind of
modification occurred:
public abstract voi
update(Observer observer, Interests interest)
Applications
The Observer pattern
is often used in the MVC – Model View
Controller models and in almost all
user interface toolkits. The Model (the data) is the Subject, the
View (the UI) is the Observer: when data changes the UI must be
updated accordingly. This way data and presentations can be reused
independently.
Consider an Excel
spreadsheet with some data and a pie
chart and a bar chart. When data changes the pie chart and bar chart
must be updated (the charts play here the role of the Observers while
the data in the spreadsheet is the Subject).
No comments:
Post a Comment