The Observer Pattern is a behavioral design pattern where:
1.One object (Subject) holds the main data.
2.Many other objects (Observers) want to be notified whenever that data changes.
This is called a "one-to-many" relationship.
Example: Weather Station & Displays
Let’s say we have a Weather Station that collects temperature, humidity, and pressure.
We also have Displays that need to show this information whenever it changes.
Implementation in C++
Step 1: Define the Observer interface
class Observer {
public:
virtual void update(float temperature, float humidity, float pressure) = 0;
virtual ~Observer() {}
};
This is an abstract class (or interface) with an update function.
All observers must implement this function to receive updates.
Step 2: Define the Subject interface
#include <vector>
class Subject {
public:
virtual void registerObserver(Observer* observer) = 0;
virtual void removeObserver(Observer* observer) = 0;
virtual void notifyObservers() = 0;
virtual ~Subject() {}
};
This interface defines how observers register, remove, or get notified.
The subject will use this to manage the observer list.
Step 3: Create the Concrete Subject (WeatherStation)
#include <iostream>
#include <algorithm>
class WeatherStation : public Subject {
private:
std::vector<Observer*> observers;
float temperature, humidity, pressure;
public:
void registerObserver(Observer* observer) override {
observers.push_back(observer);
}
void removeObserver(Observer* observer) override {
observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end());
}
void notifyObservers() override {
for (Observer* observer : observers) {
observer->update(temperature, humidity, pressure);
}
}
void setMeasurements(float temp, float hum, float pres) {
temperature = temp;
humidity = hum;
pressure = pres;
notifyObservers(); // Notify all observers of the new data
}
};
WeatherStation stores a list of observers.
When new data is set, it notifies all observers using the update() function.
Step 4: Create the Concrete Observer (Display)
class Display : public Observer {
public:
void update(float temperature, float humidity, float pressure) override {
std::cout << "Display: Temperature = " << temperature
<< "°C, Humidity = " << humidity
<< "%, Pressure = " << pressure << " hPa\n";
}
};
Display is a class that implements Observer.
When update() is called, it prints the latest weather data.
Step 5: Client Code (main function)
int main() {
WeatherStation station;
Display display1;
Display display2;
station.registerObserver(&display1);
station.registerObserver(&display2);
// First update
station.setMeasurements(25.5, 60.0, 1013.2);
// Second update
station.setMeasurements(24.8, 58.0, 1014.5);
return 0;
}
We create a WeatherStation and two Display objects.
They are registered as observers, and they get updated automatically when the data changes.
Output of the code
Display: Temperature = 25.5°C, Humidity = 60%, Pressure = 1013.2 hPa
Display: Temperature = 25.5°C, Humidity = 60%, Pressure = 1013.2 hPa
Display: Temperature = 24.8°C, Humidity = 58%, Pressure = 1014.5 hPa
Display: Temperature = 24.8°C, Humidity = 58%, Pressure = 1014.5 hPa
Advantages of the Observer Pattern in C++ Design Patterns
- Loose Coupling: Subject doesn’t care who the observers are.
- Flexible: You can add or remove observers anytime.
- Reusable: Observers can be used in different programs.
- Event-Driven: Great for live data or GUI events.
Disadvantages of the Observer Pattern in C++ Design Patterns
- Memory & Performance: Too many observers can slow things down.
- Notification Order: Not guaranteed which observer gets updated first.changes, leading to potentially unnecessary processing.
- Unwanted Updates: All observers are notified, even if not all care about every change.