Design Pattern

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.

When to use Observer Pattern?

Want some classes to react based on changes in other classes.

Resources

Formally learned in CS247.

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

  1. ConcreteSubject has its state updated
  2. notifyObservers is called - either by ConcreteSubject or some controller (like the main function)
  3. notify is called on each observer in Subject’s observer list
  4. This calls ConcreteSubject::notify, by the assumption it is pure virtual
  5. ConcreteObserver calls getState on ConcreteSubject, uses info as necessary

Example: Spreadsheet Application

  • Publishers: Cells (Subjects)
  • Subscribers: Charts (Observers)

When a cell changes, charts that are observing that cell must re-render, display new information.

May have different types of publishers / subscribers, e.g. - different charts require different logic for re-rendering.

Full Example: Twitter clone

Tweeters are subjects. Followers which are observers, 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 the main() loop. This is not mandatory, more like implementation detail.

Important Note

Notice that this only works because destruction happens in reverse order!

Wait, I’m slightly confused, why does it work? Why doesn’t the program leak memory here? Ahh, I think if you had something like declaring Follower first, then declaring Tweeter, you would get memory leak, because the Tweeter would get destroyed first. But that is not the case here, Followers gets destroyed first, then Tweeter. See Destructor.

Other Variants

One should note that other variants of the Observer pattern exist. For example, passing a Subject& via notify method.