Observer Design Pattern
“Publisher / Subscriber relationship”
The Observer pattern lets us define a subscription mechanism to notify multiple objects about any events that happen to the object that is being observed.
Resources:
- https://refactoring.guru/design-patterns/observer
- https://refactoring.guru/design-patterns/observer/cpp/example
Saw it in CS247 - Software Engineering Principles and used in A2-SimpleKit for CS349.
Pieces of the Observer Pattern:
- Publisher: Generates some data, change in state
- Subscriber: Can dynamically subscribe or unsubscribe for various publishers. Should react when data is changed.
Steps:
ConcreteSubject
has its state updatednotifyObservers
is called - either byConcreteSubject
or some controller (like themain
function)notify
is called on each observer inSubject
’s observer list- This calls
ConcreteSubject::notify
, by the assumption it is pure virtual ConcreteObserver
callsgetState
onConcreteSubject
, uses info as necessary
Twitter clone
Tweeters are subject. Followers are observers, and can only follow one tweeter.
#include <vector>
#include <string>
#include <iostream>
#include <fstream>
using namespace std;
class Observer {
public:
virtual void notify() = 0;
virtual ~Observer() {};
};
class Subject {
vector<Observer*> observers;
public:
void attach(Observer* ob) { observers.emplace_back(ob); }
void detach(Observer* ob) { /* Unfollowing todo */ }
void notifyObservers() {
for (auto ob: observers) ob->notify();
}
virtual ~Subject() = 0;
};
Subject::~Subject() {}
class Tweeter : public Subject {
ifstream in;
string lastTweet;
public:
Tweeter(const string& source): in{source} {}
bool tweet() {
getline(in, lastTweet);
return in.good();
}
string getState() { return lastTweet; }
};
class Follower : public Observer {
Tweeter* iFollow;
string myName;
public:
Follower(Tweeter* iFollow, string myName): iFollow{iFollow}, myName{myName} {
iFollow->attach(this);
}
void notify() {
string lastTweet = iFollow->getState();
if (lastTweet.find(myName) != string::npos) {
cout << "They said " << myName << ", that's my name!" << endl;
} else {
cout << "They didn't mention me, " << myName << endl;
}
}
};
int main() {
Tweeter elon{"elon.txt"};
Follower joe{&elon, "joe"};
Follower wario{&elon, "wario"};
while (elon.tweet()) {
elon.notifyObservers();
}
}
- Notice that for the above code, the
elon.tweet()
is called in themain()
loop. This is not mandatory, more like implementation detail.
Important !
From CS349
Observer Pattern:
MVC as Observer Pattern: