Observer Pattern in C++

Lokesh Bihani
4 min readApr 27, 2024

--

This is the 4th pattern of my 8 part Design Patterns in C++ series. In my previous article, I discussed the Abstract Factory Pattern in C++, and in this article, I’ll be discussing the Observer Pattern. It’s a behavioral design pattern, so all it does is handle communication between different objects. It’s the easiest and widely used pattern of all.

Observer Pattern

The Observer pattern is a behavioral design pattern that establishes a one-to-many dependency (one subject and many observers) between objects. In this pattern, when one object (the subject) changes its state, all its dependents (observers) are notified and updated automatically.

Components involved:

  1. Subject: It holds the state being observed. It maintains a list of observers and provides methods for subscribing, unsubscribing and notifying observers.
  2. Observer: This is the interface that defines the update method, which the subject calls to notify observers of any state changes.
  3. ConcreteSubject: This is a subclass of the subject class, which implements the specific behavior of subscribing, unsubscribing, and notifying observers. It also contains the actual state that observers are interested in.
  4. ConcreteObserver: This is a subclass of the observer interface that implements the update method. Each concrete observer can define its own behavior for handling updates from the subject.
UML Class diagram for Observer Pattern

Let’s see some examples.

Example 1

Note: If for some reason your requirement requires all the observers to only observe one specify ‘subject’, then it’s not mandatory to create a subject interface abstract class. Instead, you can directly create a concrete subject, as shown in the following example.

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

// Concrete Observer
class ConcreteObserver : public Observer {
public:
void update(const string& message) override {
cout << "Received message: " << message << endl;
}
};

// Publisher (Subject) class
class Publisher {
private:
vector<Observer*> observers;

public:
// Attach an observer
void subscribe(Observer* observer) {
observers.push_back(observer);
}

// Detach an observer
void unsubscribe(Observer* observer) {
// remove observer from the vector
}

// Notify all observers
void notify(const string& message) {
for (Observer* observer : observers) {
observer->update(message);
}
}
};

int main() {
Publisher publisher;

// Create subscribers
ConcreteSubscriber subscriber1;
ConcreteSubscriber subscriber2;

// Attach subscribers to the publisher
publisher.subscribe(&subscriber1);
publisher.subscribe(&subscriber2);

// Publish messages
publisher.publish("Message 1");
publisher.publish("Message 2");

return 0;
}
  • Publisher acts as the subject, responsible for managing a list of subscribers and publishing messages.
  • Subscriber defines the interface for receiving updates (messages).
  • ConcreteSubscriber implements specific behavior for handling messages.
  • In the main() function, we create a Publisher instance and two ConcreteSubscriber instances.
  • The subscribers are attached to the publisher using the attach() method.
  • Messages are published using the publish() method of the publisher, which notifies all subscribers by calling their update() method.
  • Each subscriber reacts to the message by printing it to the console, simulating message processing.

Example 2

I don’t really know how Facebook actually implements this feature, but its requirements fit the principles of the Observer pattern, so I’m using it to explain the pattern.

Suppose you’re planning a trip and for that you want to create a group chat (subject) on Facebook Messenger and add your friends (observers) to this chat so that you all can send messages (state) in this group and plan everything out at once place rather than planning separately in DMs, which would be hard to keep track of for this usecase.

class Observer {
public:
virtual ~Observer() = default;
virtual void update(const string& newMessage) = 0;
};

// Subject interface
class Subject {
public:
virtual ~Subject() = default;
virtual void subscribe(Observer* observer) = 0;
virtual void unsubscribe(Observer* observer) = 0;
virtual void notify() = 0;
};

// Concrete Subject class
class GroupChat : public Subject {
private:
vector<Observer*> observers;
vector<string> messages;
public:
void subscribe(Observer* observer) override {
observers.push_back(observer);
}

void unsubscribe(Observer* observer) override {
auto it = find(observers.begin(), observers.end(), observer);
if (it != observers.end()) {
observers.erase(it);
}
}

void notify() override {
for (Observer* observer : observers) {
observer->update(messages.back());
}
}

void sendMessage(const string& msg) {
messages.push_back(msg);
notify();
}
};

// Concrete Observer class
class Friend : public Observer {
private:
string name;
public:
Friend(const string& n) : name(n) {}

void update(const string& newMessage) override {
cout << name << " received a message: " << newMessage << endl;
}
};

int main() {
GroupChat chat;

Friend friend1("Alice");
Friend friend2("Bob");
Friend friend3("Charlie");

chat.subscribe(&friend1);
chat.subscribe(&friend2);
chat.subscribe(&friend3);

chat.sendMessage("Hey everyone, let's plan our trip!");

return 0;
}

I’ve found many implementations of Observer pattern all with minor differences like:

  1. Storing the reference to the subject inside observer.
  2. Passing the subject as argument while calling the update method on the observer.
  3. Passing custom data as argument while calling the update method on the observer.
  4. Not defining Abstract Subject class.
  5. At times, the state is defined in the Abstract Subject class, while at other times, it is defined in the concrete subject class.

All these minor subtle differences are correct, and it all depends on the initial requirements. For ex: if your requirement specifies that the object should always have access to the subject, then you’ll have to store its pointer inside the observer object, or if you don’t want your implementation of the update method to change when the internal state structure of the subject changes, passing custom data is a better option.

Summary

The Observer pattern is the easiest and most widely used behavioral pattern to implement event-based communication. It has a subject interface with subscribe, unsubscribe, and notify methods and a list of observers, a concrete subject that implements the subject interface and also maintains the state. It also has an observer interface and concrete observers that implement their own behavior of the ‘update’ method provided by the observer interface.

Real-life use cases: Github issues, Facebook messenger, Pub-sub systems, Stock market monitoring, Weather updates, etc.

--

--

Lokesh Bihani

Software Engineer passionate about System Design, DevOps, and ML. I try to simplify complex tech concepts to help others learn while deepening my own knowledge.