Observer Pattern in Depth | Master CPP Design Patterns 2025

What is the Observer Pattern?

The Observer Pattern is a behavioral design pattern that defines a one-to-many relationship between objects. When one object (called the Subject) changes its state, all its dependents (called Observers) are notified and updated automatically.

This pattern is commonly used when multiple parts of a system need to respond to changes in one part without tight coupling between components.

Real-World Analogy

Imagine you subscribe to a YouTube channel. When the creator uploads a new video, you get a notification.
Here:

  • The YouTube channel is the Subject.
  • You (the viewer) are the Observer.
  • The notification is the update sent when the Subject changes (uploads new content).

When to Use the Observer Pattern

Use this pattern when:

  • An object should automatically notify other objects when its state changes.
  • You want to reduce tight coupling between classes.
  • You need to implement an event-driven system.

Components of Observer Pattern

  1. Subject (Publisher)
    • Maintains a list of observers.
    • Provides methods to attach/detach observers.
    • Notifies observers when a change happens.
  2. Observer (Subscriber)
    • An interface or abstract class defining an update() method.
    • Concrete observers implement this method to respond to updates.
  3. ConcreteSubject
    • The actual implementation of the Subject.
    • When its state changes, it calls notify().
  4. ConcreteObserver
    • Implements the update() method to react to Subject’s state change.

C++ Implementation

Step-by-step example:

1. Observer Interface

class Observer {
public:
    virtual void update(int newValue) = 0;
    virtual ~Observer() = default;
};

2. Subject Class

#include <vector>

class Subject {
private:
    std::vector<Observer*> observers;
    int state;

public:
    void attach(Observer* obs) {
        observers.push_back(obs);
    }

    void detach(Observer* obs) {
        observers.erase(
            std::remove(observers.begin(), observers.end(), obs),
            observers.end()
        );
    }

    void notify() {
        for (auto* obs : observers) {
            obs->update(state);
        }
    }

    void setState(int value) {
        state = value;
        notify();
    }

    int getState() const {
        return state;
    }
};

3. Concrete Observers

#include <iostream>

class ConcreteObserver : public Observer {
private:
    std::string name;

public:
    ConcreteObserver(const std::string& n) : name(n) {}

    void update(int newValue) override {
        std::cout << name << " received update: " << newValue << std::endl;
    }
};

4. Main Function

int main() {
    Subject subject;

    ConcreteObserver obs1("Observer A");
    ConcreteObserver obs2("Observer B");

    subject.attach(&obs1);
    subject.attach(&obs2);

    subject.setState(42); // Both observers get notified

    subject.detach(&obs1);

    subject.setState(99); // Only Observer B gets notified

    return 0;
}

Advantages

  • Loose coupling between subject and observers.
  • Easy to add or remove observers at runtime.
  • Supports broadcast communication (one-to-many).

Disadvantages

  • Can cause performance issues if there are many observers.
  • Debugging can become tricky due to complex dependencies.
  • Risk of memory leaks if observers aren’t properly removed.

Quick Summary

ComponentRole
SubjectMaintains and notifies observers
ObserverReceives and responds to updates
ConcreteSubjectHolds the actual state
ConcreteObserverImplements response behavior

The Observer Pattern is perfect when multiple components need to stay in sync with a single source of truth .

Goal: Notify users when a new video is uploaded.

Without Observer Pattern

In this case, the YouTube channel manually knows about each user and directly notifies them. It becomes tightly coupled — hard to manage when users increase.

Code:

#include <iostream>
#include <string>

class User {
    std::string name;
public:
    User(const std::string& name) : name(name) {}
    void notify(const std::string& videoTitle) {
        std::cout << name << " received: " << videoTitle << "\n";
    }
};

class YouTubeChannel {
    User* user1;
    User* user2;  // tightly coupled to specific users
public:
    YouTubeChannel(User* u1, User* u2) : user1(u1), user2(u2) {}

    void uploadVideo(const std::string& title) {
        std::cout << "New video uploaded: " << title << "\n";
        user1->notify(title);
        user2->notify(title);
    }
};

int main() {
    User nish("Raj");
    User khushi("Mukesh");
    YouTubeChannel channel(&nish, &khushi);
    channel.uploadVideo("C++ Basics");
    return 0;
}

Problem:

  • Channel must know all users.
  • Adding/removing users needs code change.
  • Tight coupling = not flexible.

With Observer Pattern

Now, we decouple the YouTubeChannel and Users using the Observer Pattern.

Code:

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

// Observer interface
class Subscriber {
public:
    virtual void update(const std::string& videoTitle) = 0;
};

// Concrete Observer
class User : public Subscriber {
    std::string name;
public:
    User(const std::string& name) : name(name) {}
    void update(const std::string& videoTitle) override {
        std::cout << name << " received: " << videoTitle << "\n";
    }
};

// Subject
class YouTubeChannel {
    std::vector<Subscriber*> subscribers;
public:
    void subscribe(Subscriber* s) {
        subscribers.push_back(s);
    }

    void unsubscribe(Subscriber* s) {
        subscribers.erase(
            std::remove(subscribers.begin(), subscribers.end(), s),
            subscribers.end()
        );
    }

    void uploadVideo(const std::string& title) {
        std::cout << "New video uploaded: " << title << "\n";
        for (Subscriber* s : subscribers) {
            s->update(title);
        }
    }
};

int main() {
    YouTubeChannel channel;

    User nish("Raj");
    User khushi("Mukesh");

    channel.subscribe(&nish);
    channel.subscribe(&khushi);

    channel.uploadVideo("Observer Pattern in C++");

    channel.unsubscribe(&khushi);

    channel.uploadVideo("Advanced C++ Design Patterns");

    return 0;
}

Benefits:

  • YouTubeChannel is not tied to any specific users.
  • Users can dynamically subscribe/unsubscribe.
  • Easy to extend — you can have any number of subscribers.

Difference between Without Observer Pattern and Observer Pattern

FeatureWithout Observer PatternWith Observer Pattern
CouplingTightly coupledLoosely coupled
Adding/removing observersHard, manualEasy, dynamic
ScalabilityPoorExcellent
ReusabilityLowHigh

You can also Visit other tutorials of Embedded Prep 

Special thanks to @mr-raj for contributing to this article on Embedded Prep

Leave a Reply

Your email address will not be published. Required fields are marked *