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
- Subject (Publisher)
- Maintains a list of observers.
- Provides methods to attach/detach observers.
- Notifies observers when a change happens.
- Observer (Subscriber)
- An interface or abstract class defining an
update()
method. - Concrete observers implement this method to respond to updates.
- An interface or abstract class defining an
- ConcreteSubject
- The actual implementation of the Subject.
- When its state changes, it calls
notify()
.
- ConcreteObserver
- Implements the
update()
method to react to Subject’s state change.
- Implements the
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
Component | Role |
---|---|
Subject | Maintains and notifies observers |
Observer | Receives and responds to updates |
ConcreteSubject | Holds the actual state |
ConcreteObserver | Implements 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
Feature | Without Observer Pattern | With Observer Pattern |
---|---|---|
Coupling | Tightly coupled | Loosely coupled |
Adding/removing observers | Hard, manual | Easy, dynamic |
Scalability | Poor | Excellent |
Reusability | Low | High |
You can also Visit other tutorials of Embedded Prep
- What is eMMC (Embedded MultiMediaCard) memory ?
- Top 30+ I2C Interview Questions
- Bit Manipulation Interview Questions
- Structure and Union in c
- Little Endian vs. Big Endian: A Complete Guide
- Merge sort algorithm
Special thanks to @mr-raj for contributing to this article on Embedded Prep
Leave a Reply