Implementing Observer Pattern with References
How can I use references to implement a simple observer pattern in C++?
The Observer pattern is a software design pattern where an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes. We can implement this pattern in C++ using references for a clean and efficient design.
Here's a simple implementation:
#include <iostream>
#include <string>
#include <vector>
class Observer {
public:
virtual void Update(
const std::string& message) = 0;
virtual ~Observer() = default;
};
class Subject {
private:
std::vector<Observer*> observers;
std::string state;
public:
void Attach(Observer& observer) {
observers.push_back(&observer);
}
void Detach(Observer& observer) {
observers.erase(std::remove(
observers.begin(),
observers.end(),
&observer
), observers.end());
}
void SetState(const std::string& newState) {
state = newState;
Notify();
}
void Notify() {
for (auto observer : observers) {
observer->Update(state);
}
}
};
class ConcreteObserver : public Observer {
private:
std::string name;
public:
ConcreteObserver(const std::string& observerName)
: name{observerName} {}
void Update(const std::string& message) override {
std::cout << name << " received message: "
<< message << '\n';
}
};
int main() {
Subject subject;
ConcreteObserver observer1{"Observer 1"};
ConcreteObserver observer2{"Observer 2"};
subject.Attach(observer1);
subject.Attach(observer2);
subject.SetState("Hello, observers!");
subject.Detach(observer2);
subject.SetState("Observer 2 won't see this.");
}
Observer 1 received message: Hello, observers!
Observer 2 received message: Hello, observers!
Observer 1 received message: Observer 2 won't see this.
Let's break this down:
- We define an
Observer
interface with a pure virtualUpdate()
method. - The
Subject
class maintains a list of observer pointers. We use pointers here becausestd::vector
can't store references directly. - The
Attach()
andDetach()
methods inSubject
take references toObserver
objects. This provides a clean syntax for the user of the class and ensures that null observers can't be attached. - When
SetState()
is called, it updates the state and callsNotify()
, which updates all observers. ConcreteObserver
is an implementation of theObserver
interface.- In
main()
, we create a subject and two observers, attach the observers, set the subject's state (which notifies the observers), detach an observer, and set the state again.
Using references in the Attach()
and Detach()
methods provides several benefits:
- It's more intuitive: passing by reference clearly communicates that we're working with an existing object.
- It's safer: you can't pass a null reference, unlike with pointers.
- It's efficient: no need to check for null before dereferencing.
Remember, while we're using references in the interface, we're storing pointers internally. This is a common pattern when you need a mutable collection of references.
References
This lesson introduces references, explaining how they work, their benefits, and their role in performance and data manipulation