Explore the Observer Design Pattern with C++

Let’s look at how the popular Observer Design Pattern (Publish/Subscribe) can be implemented in C++ in the simplest way. Here, I’m using a weather station example to explain the Observer pattern where when the temperature, humidity and the pressure of the weather station changes, all of its clients will get notified immediately.

Weather Station Example

First let’s look at the definition of the observer pattern.

The Observer Pattern defines a one-to-many dependency between objects so that when one object changes state, all of its dependents are notified and updated automatically.

Why the Observer Pattern is important?

Think about the weather station example. It can have a set of clients (i.e. web apps, mobile apps). Each client should know whenever there is a change in the state of the weather station, otherwise the clients won’t be able to show real time accurate results. The Observer pattern is the solution for these kinds of situations. In this scenario the weather station can be taken as the Publisher (Subject) and each client can be taken as a Subscriber (Observer).

Design (Class Diagram)

Class Diagram

**See how interfaces have been used to leverage a loosely coupled design.

The important thing here is that the WeatherStation class does not need to know about the Client. All it knows is that the Observer implements the Observer interface and vice versa.

Now let’s implement this in C++

I’ll walk you through the implementation step by step you can checkout the full source code using the following GitHub link.

View Source Code on GitHub

Subject.hpp

//
// Created by shan on 4/7/17.
//
#ifndef OBSERVER_PATTERN_SUBJECT_HPP
#define OBSERVER_PATTERN_SUBJECT_HPP

#include "Observer.hpp"
/**
* Interface for the Subject
*/
class Subject {
public:
/**
* Register an observer
* @param observer the observer object to be registered
*/
virtual void registerObserver(Observer *observer) = 0;
/**
* Unregister an observer
* @param observer the observer object to be unregistered
*/
virtual void removeObserver(Observer *observer) = 0;
/**
* Notify all the registered observers when a change happens
*/
virtual void notifyObservers() = 0;
};

#endif //OBSERVER_PATTERN_SUBJECT_HPP

Observer.hpp

//
// Created by shan on 4/7/17.
//
#ifndef OBSERVER_PATTERN_OBSERVER_HPP
#define OBSERVER_PATTERN_OBSERVER_HPP
/**
* Interface for the Observer
*/
class Observer {
public:
/**
* Update the state of this observer
* @param temp new temperaure
* @param humidity new humidity
* @param pressure new pressure
*/
virtual void update(float temp, float humidity, float pressure) = 0;
};

#endif //OBSERVER_PATTERN_OBSERVER_HPP

WeatherData.hpp

//
// Created by shan on 4/7/17.
//
#ifndef OBSERVER_PATTERN_WEATHERDATA_HPP
#define OBSERVER_PATTERN_WEATHERDATA_HPP
#include <vector>
#include <algorithm>
#include <iostream>
#include "Subject.hpp"
#include "Observer.hpp"
/**
* A concrete implementation of the Subject interface
*/
class WeatherData : public Subject {
std::vector<Observer *> observers; // observers
float temp = 0.0f;
float humidity = 0.0f;
float pressure = 0.0f;
public:
void registerObserver(Observer *observer) override;
void removeObserver(Observer *observer) override;
void notifyObservers() override;
/**
* Set the new state of the weather station
* @param temp new temperature
* @param humidity new humidity
* @param pressure new pressure
*/
void setState(float temp, float humidity, float pressure);
};

#endif //OBSERVER_PATTERN_WEATHERDATA_HPP

WeatherData.cpp

//
// Created by shan on 4/7/17.
//
#include "WeatherData.hpp"
void WeatherData::registerObserver(Observer *observer) {
observers.push_back(observer);
}
void WeatherData::removeObserver(Observer *observer) {
// find the observer
auto iterator = std::find(observers.begin(), observers.end(), observer);
if (iterator != observers.end()) { // observer found
observers.erase(iterator); // remove the observer
}
}
void WeatherData::notifyObservers() {
for (Observer *observer : observers) { // notify all observers
observer->update(temp, humidity, pressure);
}
}
void WeatherData::setState(float temp, float humidity, float pressure) {
this->temp = temp;
this->humidity = humidity;
this->pressure = pressure;
std::cout << std::endl;
notifyObservers();
}

Client.hpp

//
// Created by shan on 4/7/17.
//
#ifndef OBSERVER_PATTERN_CLIENT_1_HPP
#define OBSERVER_PATTERN_CLIENT_1_HPP
#include <iostream>
#include "Observer.hpp"
/**
* a client that implements the Observer interface
*/
class Client : public Observer {
int id;
public:
Client(int id);
virtual void update(float temp, float humidity, float pressure) override;
};

#endif //OBSERVER_PATTERN_CLIENT_1_HPP

Client.cpp

//
// Created by shan on 4/7/17.
//
#include "Client.hpp"
void Client::update(float temp, float humidity, float pressure) {
// print the changed values
std::cout << "---Client (" << id << ") Data---\tTemperature: " << temp
<< "\tHumidity: " << humidity
<< "\tPressure: " << pressure
<< std::endl;
}
Client::Client(int id) {
this->id = id;
}

Yeah! It’s done, let’s go for a test drive….

[caption id=”” align=”aligncenter” width=”640"]

An overview of what’s gonna happen

Here is the main class which you need to run for the test drive.

main.cpp

#include <iostream>
#include "WeatherData.hpp"
#include "Client.hpp"

int main() {
WeatherData weatherStation;
Client one(1), two(2), three(3);
float temp, humidity, pressure;
weatherStation.registerObserver(&one);
weatherStation.registerObserver(&two);
weatherStation.registerObserver(&three);
std::cout << "Enter Temperature, Humidity, Pressure (seperated by spaces) << ";
std::cin >> temp >> humidity >> pressure;
weatherStation.setState(temp,humidity,pressure);
weatherStation.removeObserver(&two);
std::cout << "\n\nEnter Temperature, Humidity, Pressure (seperated by spaces) << ";
std::cin >> temp >> humidity >> pressure;
weatherStation.setState(temp,humidity,pressure);
return 0;
}

The Output

Console Output

See you with another tutorial.
Please comment below if you have any issues.

Good Bye