|
Name of Pattern - Observer
Also Known As
- Dependent, Publish-Subscribe
Definition
- Defines a one-to-many relation between one object (called subject or
publisher) and its dependent objects (called observers or subscribers), such
that when subject's state changes, then the observers are notified of the change
so that they can update their states with the subject's new state.
When to use -
Observer pattern is used when the following conditions are satisfied: -
1. One or more
objects depend on one object. The dependent objects are called as observers and
the object on which they are dependent is called as subject. As you might have
noticed, the relation between subject and observer is one-to-many.
2. We want to
reuse subject and any observer independently of other objects.
3. Subject does
not know internal detail of observers but should still be able to notify
observers if any change happens in subject's state.
4. The number of
observers is dynamic.
Sample Test
Scenario - Let us consider a scenario in which these conditions are
satisfied.
There is a table
containing some data. Based on these data, you want to display various
statistical diagrams. Let us assume that the diagrams are histogram and pie
chart. When the data are changed, the histogram and the pie chart should be
updated accordingly.
Let us see how one
might go about fulfilling these requirements without using Observer pattern.
OK, you may be new
to observer pattern. But you are object-oriented programmer. Being
object-oriented programmer you like to create objects. You have come up with
following design: -
Make a class for
holding data. Let us call this class as DataHolder. Have two more classes named
HistogramMaker and PieChartMaker. HistogramMaker has a public method named
MakeHistogram and PieChartMaker has a public method named MakePieChart.
HistogramMaker and PieChartMaker depend on DataHolder.
When data changes,
then DataHolder calls MakeHistogram of HistogramMaker and MakePieChart of
PieChartMaker.
This solution will
work. It will draw histogram and pie chart based on latest data. But what if, in
future, you want to create a new kind of diagram, say ogive? For that you need
to create a new class that creates ogive and you will have to make code changes
in DataHolder class to handle this new class now. Only for adding a new
dependent class, you need to make code change in DataHolder class.
What if, for some
customer, you do not want to support some diagram. You need to delete or comment
out codes that correspond to that diagram. Again, code change.
What if, at run
time, you decide to add or remove some diagram? The solution does not work in
this case at all because the names of the dependent classes and their functions
have been hardcode inside DataHolder class.
What if you want
to reuse the DataHolder class in some other product, which does not want to
display histogram and pie chart? Can you reuse DataHolder as it is? You cannot
because DataHolder contains source codes for histogram and pie chart.
All of these
problems are because of tight coupling in which two or more classes are so much
dependent on one another that any one cannot be reused without the other.
Would it not be
great to have a design solution that takes care of all these problems? Yes,
there is and the solution is Observer design pattern.
Class
Structure in Observer Pattern

src="observer_files/image001.gif" v:shapes="Image19">
Explanation:
- ConcreteSubject inherits from Subject. ConcreteObserver inherits from
Observer.
Subject maintains
an array of Observer instances. An observer can be added to array through Attach
method and removed from array through Detach method.
When state change
happens in Subject it calls Update method of all Observers in the array in its
Notify method. It is up to an observer to decide what to do in the Update
method. When Update of an observer is invoked, then the observer can get latest
state of the subject by calling GetState. The state of subject can be changed
through SetState method.
Note that an
element in observers array is of type Observer and not ConcreteObserver. Attach
and Detach take Observer as argument and not ConcreteObserver. This is an
example of 'program to interface and not to implementation.) By following the
principle of 'program to interface and not to implementation' we have achieved
loose coupling between a ConcreteSubject and a ConcreteObserver.
If a function
takes any class instance as an argument, it will also accept an instance of any
child class. Therefore, you can pass an instance of ConcreteObserver to Attach
and Detach.
Nowhere in
ConcreteSubject have we made explicit mention of ConcreteObserver. Likewise,
nowhere we have made mention of ConcreteSubject in ConcreteObserver. Therefore,
ConcreteObserver and ConcreteSubject can be reused independently of each other.
At run time, any
new observer can be added to the array or any existing observer can be removed
from the array. Both of these can be done without making any code change in
Subject, ConcreteSubject or existing observers.
The following
sequence diagram shows the interaction between a subject and an observer. In the
diagram the subject is a concrete subject and the object is a concrete object.

(Sequence of calls in Observer pattern)
Revisiting Our
Requirements: -Let us see how we can use Observer pattern when we want to
display histogram and pie chart. The class structure is as follows.
Here DataHolder is
a ConcreteSubject. HistogramMaker and PieChartMaker are ConcreteObserver
classes.
Inside Update,
HistogramMaker displays histogram; Update of PieChartMaker displays pie chart.
But the functionalities of displaying histogram and of displaying pie chart are
private to these HistogramMaker and PieChartMaker respectively. DataHolder does
not care what a ConcreteObserver does inside its Update method.
Any
ConcreteObserver can be added or removed from the list of observers to whom
DataHolder should update of its changes at run time. Do you want to add a new
class (say OgiveMaker) that can display Ogive without making any code change in
DataHolder or any of the existing ConcreteObserver classes? No problem. Create a
new class OgiveMaker. Inherit it from Observer. Inside Update of OgiveMaker
write code to display Ogive. Call Attach of Subject passing an instance of
OgiveMaker.
Implementation Issues
Where does
subject store its observers? As mentioned earlier observer pattern is used
when there is one subject and one or more observers. In such a case, the subject
maintains an array of observers. However, in practice, there may be scenarios in
which an observer may need to get notifications from more than one subject.
Where should subjects store their list of observers? One solution is to let each
subject maintain its own array of observers. But this is memory-inefficient
because two or more subjects may have some common observers. If all of these
subjects store these observers in their arrays, then there will be multiple
copies of these observers.
This problem can
be solved by using a central hash table. Subjects will not store observers in
their private arrays. Rather, they will use the hash table. But the downslide of
using hash table is that retrieving data from a hash table is slower than from
an array. So, you have trade-off between memory usage and execution time.
How does an
obsever know which subject is calling the observer's update? Again consider
the case in which an object may be updated by more than one subject. The Update
method may need to know which subject is sending the notification. One way is
that the subject may pass itself as a parameter to the Update method.
Alternatively, the subject may pass some unique identifier to the Update method.
Dangling
references to deleted subjects. Suppose the subject gets destroyed. What
will happen to its observers? Observers now have dangling reference to the
subject. The will unnecessarily keep waiting for notifications from the subject.
Worse, some observer may even call some method (e.g. SetState) on subject. This
will make your progam crash. Therefore, when the subject is being destroyed, the
observers should be notified about this.
We talked about
the scenario in which subject is destroyed. What if an observer is destroyed
before the subject? If the subject does not know that the observer has been
destroyed, then the subject has dangling reference to the observer. The subject
may continue to call Update on the observer. This will result in run-time error.
Therefore, when an observer is being destroyed, then the subject's Detach method
should be called so that the subject can remove the observer from its list.
Push and Pull
models - How does observer know about subject's state? In push model, the
subject gives (pushes) its state to observer via Update method. In pull model,
the subject send minimum change information in Update method only to tell
observers that the subject's state has changed. The observers call GetState of
subject to get whatever information they need. They can pass a parameter to
GetState indicating the type of required information.
Push model is more
efficient so far execution time is concerned. This is because observers do not
need to call GetState. However, the disadvantage in push model is that subject
may give lots of information to an observer, which the observer does not need.
This is memory-inefficient. If subject sends only as much information to an
observer as the observer needs, then the subject needs to know the details of an
observer. This introduces tight coupling between the subject and the observer -
a problem which the observer pattern is supposed to solve.
Pull model
maintains loose coupling between the subject and an observer. But this requires
extra GetState call.
In practice, often
a happy medien between the two is used. Subject sends as much information in
Update as is required by all observers. The observers get the rest by calling
GetState method. In this approach, the subject does not need to know what
information a particular observer needs. But, it does need to know the common
information that all observers need. This requires tighter coupling than in pull
model. But this is a trade-off between reusability and efficiency, which is
often adopted in practice.
The sequence of
calls in Push model is the same as the sequence diagram shown above (with
subject's new state) passed in Update method. The sequence of calls in Pull
model is as shown below: -

(Sequence diagram showing Pull model)
Specifying
Changes of Interest - An observer may specify that kind of changes it is
interested in by passing a parameter in Attach method. When a change happens in
subject's state, the subject notifies to only those observers who specified
interest in that change. As a real-life example, assume that subject contains
details of transactions in a bank. There are different objects to deal with
different kind of transactions, viz, credit cards, savings bank a/c, current
a/c, bank loans etc. All of these objects are observers. But they are interested
in different kinds of transactions. If some transaction related to credit card
happens, then only the observers which are interested in such a transaction
should be updated.
Who triggers
the update (Notify operation in Subject)?
There are two
options.
1. Insert the call to Notify()
in the client right after each call to Subject that affects an internal
state change. This gives the client full control over the frequency of the
notification, but also adds extra responsibility to the client, which can lead
to errors if the developer forgets to call Notify().
2. Encapsulate the
call to Notify() inside each state-changing operation of Subject.
This way, a state change always causes Notify() to be called without
additional action from the client. The downside is that several nested
operations might cause multiple notifications. The following diagram shows a
scenario in which multiple updates may be triggered.

(Scenario of extraneous updates)
Calling multiple
updates for a single, but nested operation can cause some inefficiency, but also
leads to more serious side effects: The subject could be in an invalid state
when the nested Notify method is invoked at the end of Operation B (see
the above diagram) because Operation A has only been processed part of the way.
In this case, the nested notify should be avoided. For example, Operation B can
be extracted out into a method without notification logic and can rely on the
call to Notify() inside Operation A.
Observers Affecting State Change
In some cases, an observer may
change the state of the subject while it is processing the update() call.
This could lead to problems if the subject automatically calls Notify()
after each state change. The following diagram shows why.

(Modifying object state from within Update causes an infinite loop)
In this example, the observer
performs Operation A in response to the state change notification. If Operation
A changes the state of DomainObject, it then triggers another call to
Notify(), which in turn calls the Update method of the observer
again. This results in an infinite loop. The infinite loop is easy to spot in
this simple example, but if relationships are more complicated, it may be
difficult to determine the dependency chain. One way to reduce the likelihood of
infinite loops is to make the notification interest-specific.
Maintaining self consistency of the
Subject
Self-consistency is all about
retaining state of the Subject after notifying the Observers. It is important to
make sure that the Subject’s state is self-consistent before and after calling
Notify method to keep the state of Observers in sync with
the Subject. It is difficult to
maintain self-consistency in inherited operations, that too, when Notify method
is called in the base class. A simple example is shown in Example-1
INT CDervSubject::Calculate( int nVal )
CBaseSubject::Calculate( nVal );
In this example, CDervSubject
overrides the operation Calculate and calls the base class method, which sends
the notification to all the Observers. The derived class implementation can
change the value of the data member m_nResult, bringing in the problem of
self-inconsistency. To avoid this, GoF suggests the use of Template methods.
Primitive operations can be defined as protected virtual methods that can be
overridden by the derived classes and call the Notify method as the last
operation in the template method to maintain self-consistency. .
INT CBaseSubject::Calculate( int nVal )
INT CBaseSubject::DoCalculate( int nVal )
INT CDervSubject::DoCalculate( int nVal )
CBaseSubject::DoCalculate( nVal );
Avoiding redundant notifications
Sometimes, calling Notify method in
each and every state changing method is not necessary. A single Notify call
after all the state changes will avoid redundant notification calls. In
Example-3 the Observer receives three notification calls instead of one.
void CMySubject::SetFont( Font & rFont )
void CMySubject::SetTextColor( Color & rTextColor )
m_TextColor = rTextColor;
void CMySubject::SetBkColor( Color & rBkColor )
void CMySubject::SetAttributes( Font & rFont, Color
& rTextColor, Color & rBkColor )
SetTextColor( rTextColor );
SetBkColor( rTextColor );
void CMyObserver::SetAttributes()
m_MySubject.SetAttributes( Font,TextColor, BkColor );
To ensure a single notification
call at the end of all state changes, CMySubject should inherit from
CChangeTracker and implement the methods. Instead of calling Notify method in
all state changing methods, CMySubject calls StartChange before making the
change and FinishChange after making the change. A change reference count is
incremented during StartChange and decremented during FinishChange and Notify
method is called only when the reference count reaches 0. The advantage of this
approach is any number of state changing methods can be called in any sequence
and the Observers will be notified only once at the end of all the state
changes.This can be achieved by using a Change Tracker class that has a set of
Change Tracking methods
virtual void StartChange() = 0 ;
virtual void FinishChange() = 0;
class CMySubject : public CSubject,
virtual void StartChange();
virtual void FinishChange();
void CMySubject::StartChange()
void CMySubject::FinishChange()
if( m_nChangeCount == 0 )
void CMySubject::SetFont( Font & rFont )
void CMySubject::SetTextColor( Color & rTextColor )
m_TextColor = rTextColor;
void CMySubject::SetBkColor( Color & rBkColor )
void CMySubject::SetAttributes( Font & rFont, Color
& rTextColor, Color & rBkColor )
SetTextColor( rTextColor );
SetBkColor( rTextColor );
Benefits of Observer Pattern
-
Avoid Direct Interactions-The Observer Pattern
avoids direct object interactions and can be used when one or more objects are
interested in the state changes of a given object.
-
Loosely Coupled-It can be used to develop loosely
coupled applications maintaining object state dependencies.
-
Number of State-It can be used when the number of
state dependent objects is not known in advance or even when the number may
even change over time.
-
Reusability-Subject and the Observer objects can be
reused separately and they can vary independently.
-
Layers-The Observer Pattern can be used to develop
application in layers.
-
Addition and Deletion-Since all the Observers are
notified through the same abstract operation, it is easy to add or remove
Observers on demand.
-
Overhead-Subject should maintain a list of
Observers. In some cases, the Observers may request only for specific events
for which the Subject has to notify them. Subject should also maintain this
information along with the list of Observers, which increases the overhead.
Liabilities in Observer Pattern
-
Notification Clue-Observers will simply be only
notified about a state change. It is up to the Observer to find out what
exactly has changed. However, this can be avoided by including additional
information along with the notification. This will give some clue about the
change to the Observer.
-
State Inconsistency-Observer objects are totally
independent and they have no knowledge on the existence of the fellow
Observers. Therefore, an Observer object can change the state of the Subject
before even all the Observers are notified. This may result in state
inconsistencies and the state change notifications will be lost.
-
Recursive Updates-Whenever an Observer changes the
Subject’s state, all the dependent Observers are notified. If the dependency
criteria is not well defined, recursive updates can easily happen.
-
Understandability-Observer Pattern introduces an
additional level of indirection to maintain state consistency. This increases
the flexibility in the application design, but has a sure performance impact.
Also, too many indirections decrease the understandability of the code.
-
Dangling Reference-When the Subject is deleted, the
Observers don’t have a direct way to know about the deletion. Therefore, the
Observers will have dangling reference to the deleted Subjects.
References
Erich
Gamma, Richard Helm, Ralph Johnson and John Vlissides Design Patterns
Elements of Reusable Object-Oriented Software Addison-Wesley, 1995
|