Visharad Software Visharad Software

                                                                                                                  
 

     
 

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

Example-1 Implementing Self consistency violation in the Subject

INT CDervSubject::Calculate( int nVal )
{
    // Call the base class method, which implements 
              a complicated 

    // calculation algorithm and sets the data member 
                              m_nResult
    CBaseSubject::Calculate( nVal );    
    // Calling this method sends a
     // notification to the Observers          
    // Specific implementation for the derived class
    if( m_nResult > 1000 )
    {
        m_nResult %= 1000;
    }
    return 0;
}

 

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. .

Maintaining self consistency of the Subject Using Template method

INT CBaseSubject::Calculate( int nVal )
{
    // Call DoCalculate that can be redefined in d
      erived classes.
    // DoCalculate is a protected virtual method
    DoCalculate( nVal );
    // Notify the Observers about the change
    Notify();
    return 0;
}
INT CBaseSubject::DoCalculate( int nVal )
{
    // Do calculation and set m_nResult
    return 0;
}
INT CDervSubject::DoCalculate( int nVal )
{
    // Call base class method
    CBaseSubject::DoCalculate( nVal );
    // Specific implementation for the derived class
    if( m_nResult > 1000 )
    {
        m_nResult %= 1000;
    }
    return 0;
}
 
 
 
 

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.

Example-3 Implement Redundant notifications

 

void CMySubject::SetFont( Font & rFont )
{
    ...
    ...
    // Update member
    m_Font = rFont;
    ...
    ...
    // Notify the Observers
    Notify();
}
 
void CMySubject::SetTextColor( Color & rTextColor )
{
    ...
    ...
    // Update member
    m_TextColor = rTextColor;
    ...
    ...
    // Notify the Observers
    Notify();
}
 
void CMySubject::SetBkColor( Color & rBkColor )
{
    ...
    ...
    // Update member
    m_BkColor = rBkColor;
    ...
    ...
    // Notify the Observers
Notify();
}
 
void CMySubject::SetAttributes( Font & rFont, Color
                    & rTextColor, Color & rBkColor )

                                
{
    // Call SetFont method
    SetFont( rFont );
    // Call SetTextColor method
    SetTextColor( rTextColor );
    // Call SetBkColor method
    SetBkColor( rTextColor );
}
 
// Observer code
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

Implementing Notification using Change Tracker

class CChangeTracker
{
    protected :
        virtual void StartChange() = 0 ;
        virtual void FinishChange() = 0;
};
 
// CMySubject inherits from CSubject and 
                                 CChangeTracker
class CMySubject : public CSubject, 
                        protected CChangeTracker
{
    protected :
        virtual void StartChange();
        virtual void FinishChange();
    private :
        INT m_nChangeCount;
};
 
void CMySubject::StartChange()
{
    m_nChangeCount++;
}
 
void CMySubject::FinishChange()
{
    m_nChangeCount--;
    if( m_nChangeCount == 0 )
    {
        Notify();
    }
}
 
// State change operations
void CMySubject::SetFont( Font & rFont )
{
    // call StartChange
    StartChange();
...
    ...
    // Update member
    m_Font = rFont;
    ...
    ...
    // call EndChange
    EndChange();
}
 
void CMySubject::SetTextColor( Color & rTextColor )
{
    // call StartChange
    StartChange();
    ...
    ...
    // Update member
    m_TextColor = rTextColor;
    ...
    ...
    // call EndChange
    EndChange();
}
 
void CMySubject::SetBkColor( Color & rBkColor )
{
    // call StartChange
    StartChange();
    ...
    ...
    // Update member
    m_BkColor = rBkColor;
    ...
    ...
    // call EndChange
    EndChange();
}
void CMySubject::SetAttributes( Font & rFont, Color 
                      & rTextColor,   Color & rBkColor )

                              
{
    // call StartChange
    StartChange();
    // call SetFont method
    SetFont( rFont );
    // call SetTextColor method
    SetTextColor( rTextColor );
    // call SetBkColor method
    SetBkColor( rTextColor );
    // call EndChange
    EndChange();
}
 

Benefits of Observer Pattern

  1. 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.

  2. Loosely Coupled-It can be used to develop loosely coupled applications maintaining object state dependencies.

  3. 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.

  4. Reusability-Subject and the Observer objects can be reused separately and they can vary independently.

  5. Layers-The Observer Pattern can be used to develop application in layers.

  6. Addition and Deletion-Since all the Observers are notified through the same abstract operation, it is easy to add or remove Observers on demand.

  7. 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

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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