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.

Resources:

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:

  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

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 the main() loop. This is not mandatory, more like implementation detail.

Important !

From CS349

Observer Pattern:

MVC as Observer Pattern: