Understanding Observer Pattern in C++ in depth


Reading time: 20 minutes

The Observer Pattern is a design pattern where changes in one object can be notified to other objects. This is important as with this, we can use objects that have dependency and can avoid regular checking for changes. It is widely used in situations like refreshing a webpage provided the database state has changed like a stock price page.

Key points:

  • Observer is the object to whom the changes will be notified
  • Subject is the object in which changes will take place
  • There is One to many dependency between Subject and Observer
  • Observers themselves do not have access to data
topic of image

We will answer the following questions:

  • When to use Observer pattern?
  • What are Real Life Use cases of Observer pattern?
  • A C++ Exmaple of Observer pattern
  • What are the Advantages of Observer pattern?
  • What are the Disadvantages of Observer pattern?

When to use Observer pattern?

You should consider using this pattern in your application when multiple objects are dependent on the state of one object as it provides a neat and well tested design for the same.

topic of image

What are Real Life Use case of Observer pattern?

  1. It is heavily used in GUI toolkits and event listener.

  2. In java:

    • the button(subject)
    • onClickListener(observer) are modelled with observer pattern.
  3. Social media, RSS feeds, email subscription in which you have the option to follow or subscribe and you receive latest notification.

  4. All users of an app on Play store and iOS Store gets notified if there is an update.

  5. All chrome extension users are updated when a new version is available

A C++ Exmaple of Observer pattern

Here we have:

  • Observer class with a notify function
  • observer_concrete class which extends the Observer class
  • subject class which keeps track of all observers and calls the notify() function of each observer in case of any changes

Go through this C++ example code carefully:

#include <vector>
#include <functional>
class observer
{
  public:
    virtual void notify() = 0;
};
class observer_concrete : public observer
{
  public:
    virtual void notify() override
    { }
};
class subject
{
  public:
    void register_observer(observer& o)
    {
      observers.push_back(o);
    }
    void notify_observers()
    {
      for (observer& o : observers) {
        o.notify();
      }
    }
  private:
    std::vector<std::reference_wrapper<observer>> observers;
};

The observer pattern allows generic observer objects to be registered with a subject object and receive notifications when certain events occur on them.

Observers (also known as listeners), in this case, are objects that implement the observer interface. The register_observer function adds observers to this std::vector, which are later to be notified by the notify_observers function.

Use of std::reference_wrapper for the elements of the std::vector , because the standard containers require the element type to be assignable, which normal reference types are not.

What are the Advantages of Observer pattern?

Provides a loosely coupled design between objects that interact. Loosely coupled objects are flexible with changing requirements. Here loose coupling means that the interacting objects should have less information about each other.

Observer pattern provides this loose coupling as:

  • Subject only knows that observer implement Observer interface.Nothing more.
  • There is no need to modify Subject to add or remove observers.
  • We can reuse subject and observer classes independently of each other.

What are the Disadvantages of Observer pattern?

  • Memory leaks caused by Lapsed listener problem because of explicit register and unregistering of observers.It originates in the observer pattern, where observers (or listeners) register with a subject (or publisher) to receive events. In basic implementation, this requires both explicit registration and explicit deregistration, as in the dispose pattern, because the subject holds strong references to the observers, keeping them alive. The leak happens when an observer fails to unsubscribe from the subject when it no longer needs to listen.

Consequently, the subject still holds a reference to the observer which prevents it from being garbage collected — including all other objects it is referring to — for as long as the subject is alive, which could be until the end of the application.

  • Infinite recursion in notifications

This occurs when there is a cycle in the dependency graph. Now technically cycles should be impossible, and that is indeed the case if data dependency is represented at a fine-grained enough level. However in practice it is common for objects to host multiple caches, so that there may be cycles at the coarse resolution of objects.

  • Dirty reads
    Let Y = f(X) and Z = g(X,Y). This typically leads to a redundant edge in the dependency graph:
topic of image

Observer pattern dirty reads:

Let X be changed and X notifies Z before Y. Then Z may access a dirty cached value in Y, because Y hasn't yet been marked as dirty by X.

A dirty read can be a source of surprises. For example, Z may see a value of Y that should be impossible given the value of X.

Observer attaches or detaches during a notification

The problem is that while a subject is notifying one of the observers, an observer decides to attach or detach to/from the subject. This may cause a crash depending on the data structure used by the subject to store the observers.

One hack I've seen to solve this problem is to use a "one shot observer". Another is to make a copy of the list of observers before issuing the notifications.

Question on Observer Pattern

Which design pattern defines one-to-many dependency among objects?

Observer pattern
Singleton pattern
Facade Pattern
Factory method pattern
It defines one-to-many dependency among objects so that when one object changes its state, all its dependents are notified.