The Observer Pattern

Contents[Show]

The Observer Pattern is a behavioral pattern from the book   "Design Patterns: Elements of Reusable Object-Oriented Software". It defines 1-to-n dependencies between objects so that changes to one object cause all dependent objects to be notified.

 BehavioralPattern

The Observer Pattern solves a classical design issue: How can you ensure that all prospects are automatically notified if an important event has taken place?

The Observer Pattern

Purpose

  • Defines 1-to-n dependencies between objects so that changes to one object cause all dependent objects to be notified.

Also known as

  • Publisher-Subscriber (short Pub/Sub)

Use Case

  • One abstraction depends on the state of another abstraction
  • A change to one object implies a change to another object
  • Objects should be notified of state changes of another object without being tightly coupled

Structure

Observer

 Subject

  • Manages its collection of observers
  • Allows the observers to register and unregister themself

Observer

  • Defines an interface to notify the observers

ConcreteObserver

  • Implements the interface
  • Is notified by the Subject

 

Rainer D 6 P2 540x540Modernes C++ Mentoring

Be part of my mentoring programs:

 

 

 

 

Do you want to stay informed about my mentoring programs: Subscribe via E-Mail.

Example

The following program observer.cpp directly implements the previous class diagram.

// observer.cpp

#include <iostream>
#include <list>
#include <string>

class Observer {
 public:
  virtual ~Observer(){};
  virtual void notify() const = 0;
};

class Subject {
 public:
  void registerObserver(Observer* observer) {
    observers.push_back(observer);
  }
  void unregisterObserver(Observer* observer) {
    observers.remove(observer);
  }
  void notifyObservers() const {                             // (2)
    for (auto observer: observers) observer->notify();
  }

 private:
  std::list<Observer *> observers;
};

class ConcreteObserverA : public Observer {
 public:
    ConcreteObserverA(Subject& subject) : subject_(subject) {
        subject_.registerObserver(this);
    }
    void notify() const override {
        std::cout << "ConcreteObserverA::notify\n";
    }
 private: 
    Subject& subject_;                                    // (3)
};

class ConcreteObserverB : public Observer {
 public:
    ConcreteObserverB(Subject& subject) : subject_(subject) {
        subject_.registerObserver(this);
    }
    void notify() const override {
        std::cout << "ConcreteObserverB::notify\n";
    }
 private: 
    Subject& subject_;                                   // (4)
};


int main() {

    std::cout << '\n';

Subject subject; ConcreteObserverA observerA(subject); ConcreteObserverB observerB(subject); subject.notifyObservers(); std::cout << " subject.unregisterObserver(observerA)\n"; subject.unregisterObserver(&observerA); // (1) subject.notifyObservers();

std::cout << '\n'; }

 

The Observer supports the member function notify, and the Subject supports the member functions registerObserver, unregisterObserver, and notifyObservers. The concrete observers receive the subject in their constructor and use them to register themself for the notification. They have a reference to the subject (lines 3 and 4).  Only observerA is unregistered in line (1). The member function notifyObservers goes through all registered observers and notifies them (line 2).

The following screenshot shows the output of the program:

observer

By the way, you may have noticed that I used no memory allocation in the previous program observer.cpp. This is how virtuality is typically used if you aren't allowed to allocate memory, such as in deeply embedded systems. Here is the corresponding main function using memory allocation:

int main() {

std::cout << '\n'; Subject* subject = new Subject; Observer* observerA = new ConcreteObserverA(*subject); Observer* observerB = new ConcreteObserverB(*subject); subject->notifyObservers(); std::cout << " subject->unregisterObserver(observerA)" << "\n"; subject->unregisterObserver(observerA); subject->notifyObservers(); delete observerA; delete observerB; delete subject;

std::cout << '\n'; }

 

Know Uses

The Observer Patten is often used in architectural patterns such as Model-View-Controller (MVC)  for graphical user interfaces or Reactor for event handling.

  • Model-View-Controller: The model represents the data and its logic. The model notifies its dependent component, such as the views. The views are responsible for representing the data, and the controller is for the user input.
  • Reactor: The Reactor registers the event handlers. The synchronous event demultiplexer (select) notifies the handles if an event occurs.

I will dedicate an entire future post to both architectural patterns.

Variations

The Subject in the program observer.cpp simply sends a notification. However, more advanced workflows are often implemented:

The Subject sends a

  • value.
  • notification that a value is available. Afterward, the Observer has to pick it up.
  • notification, including an indication of which value is available. The Observer picks it up if necessary.
  • The Mediator Pattern establishes communication between the sender and the receiver. Each communication between the two endpoints goes, therefore, through the mediator. The Mediator and the Observer are pretty similar. The goal of the mediator is to decouple the sender and the receiver. On the contrary, the Observer established a one-way communication between the publisher and the subscriber.

Pros and Cons

Pros

  • New observers (subscribes) can easily be added to the publisher
  • Observers can register and unregister themself at run time

Cons

  • Neither does the publisher provides a guarantee in which order the subscribers are notified, nor does it gives the assertion of how long the notification takes when you have many subscribers.
  • The publisher may send a notification, but a subscriber is not alive anymore. To avoid this drawback, you can implement the destructor of the concrete observers in such a way that the concrete observers unregister themself in its destructor:
class ConcreteObserverA : public Observer {
 public:
    ConcreteObserverA(Subject& subject) : subject_(subject) {
        subject_.registerObserver(this);
    }
    ~ConcreteObserverA() noexcept {
        subject_.unregisterObserver(this);
    }
    void notify() const override {
        std::cout << "ConcreteObserverA::notify\n";
    }
 private: 
    Subject& subject_;
};

 

The concrete observer ConcreteObserverA models the RAII Idiom: It registers itself in its constructor and unregisters itself in its destructor.

What's next?

The Visitor Pattern has an ambivalent reputation. On one hand, enables the Visitor Double Dispatch. On the other hand, the Visitor is pretty complicated to implement. Let me introduce the Visitor Pattern in my next post.

Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Animus24, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Kris Kafka, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Matthieu Bolt, Stephen Kelley, Kyle Dean, Tusar Palauri, Dmitry Farberov, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, and Rob North.

 

Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, and Slavko Radman.

 

 

My special thanks to Embarcadero CBUIDER STUDIO FINAL ICONS 1024 Small

 

My special thanks to PVS-Studio PVC Logo

 

My special thanks to Tipi.build tipi.build logo

 

My special thanks to Take Up Code TakeUpCode 450 60

 

Seminars

I'm happy to give online seminars or face-to-face seminars worldwide. Please call me if you have any questions.

Bookable (Online)

German

Standard Seminars (English/German)

Here is a compilation of my standard seminars. These seminars are only meant to give you a first orientation.

  • C++ - The Core Language
  • C++ - The Standard Library
  • C++ - Compact
  • C++11 and C++14
  • Concurrency with Modern C++
  • Design Pattern and Architectural Pattern with C++
  • Embedded Programming with Modern C++
  • Generic Programming (Templates) with C++

New

  • Clean Code with Modern C++
  • C++20

Contact Me

Modernes C++,

RainerGrimmDunkelBlauSmall

Stay Informed about my Mentoring

 

Mentoring

English Books

Course: Modern C++ Concurrency in Practice

Course: C++ Standard Library including C++14 & C++17

Course: Embedded Programming with Modern C++

Course: Generic Programming (Templates)

Course: C++ Fundamentals for Professionals

Course: The All-in-One Guide to C++20

Course: Master Software Design Patterns and Architecture in C++

Subscribe to the newsletter (+ pdf bundle)

All tags

Blog archive

Source Code

Visitors

Today 4079

Yesterday 4344

Week 40957

Month 21203

All 12099412

Currently are 149 guests and no members online

Kubik-Rubik Joomla! Extensions

Latest comments