Lecture 18

Pointers and references to an abstract class can be declared.

Observer Pattern

  • publish-subscribe model
  • One class: publisher/subject - generates data
  • One or more classes: subscribe/observer classes - receive and react to that data
  • *Example: **publisher spreadsheet cells, observers are graphs or formula cells, when the cells change, graphs/formula cells update themselves.

There can be may different types of observer classes, and the subject should not need to know all the details about them.

Observer Pattern: UML


The abstract class subject contains all the code common to all subjects - the abstract observer contains the interface common to all observers

Sequence of method calls:

  1. Subject’s state is changed
  2. Subject::notifyObservers is called (either by the concrete subject when the change happens, or by the client when they change the subject.
  3. notifyObservers calls notify on each observer
  4. Each ConcreteObserver::noitfy gets the data from their subject (e.g. with getState) and react accordingly

Example: Lets write some code on horse races. The subject publishes the winner, the observers are individual betters who react to winning or losing

class Subject {
  vector<observer *> observes;
public:
  Subject() {} //would acc implement in .cc file
  void attach(Observer *ob) {observes.emplace_back(ob);}
  void detatch(Observer *ob) {
    // assume each * shows up only once in our vector
    auto it = observers.begin();
    for (it ; it != observers.end() && *it!=ob; ++it);
 
    if (it == observers.end()) return;
    observers.erase(it);
  }
 
  void notifyObservers() {
    for (auto p:observers) p->notify();
  }
 
  virtual ~Subject() = 0; // makes this pire virtual, but still need to implement this as it is desturctor, we do this outside of class
};
 
Subject::~Subject() {} // need this for linking

If you want a class to be abstract, but don’t naturally have a method to make pure virtual, make the destructor pure virtual (since it should always be virtual if you have inheritance). However, the destructor still needs an implementation, else, the code won’t link. Hence, we implement it outside of the class body.

Now, lets write the Observer class:

class Observer {
public:
  virtual void notify() = 0;
  virtual ~observer() {}
};

Now, lets write our concrete classes:

class HorseRace : public Subject {
  ifstream in; // source of data
  string lastWinner;
public:
  HorseRace(const String &source): in{source} {}
  ~HorseRace () {}
  bool runRace () {
    bool b = in >> lastWinner;
    notifyObserver();
    return b;
  }
  string getState() const {return lastWinner;}
};
 
// Now, we write our better class
class Better: public Observer {
  HorseRace *hr;
  string nyName, horse;
public:
  Bettor (...) ... {
    hr->attach(this); // tells subject we are observing it
  }
 
  void notify() {
    string winner = hr->getState();
    cout << myName << (winner == horse? "wins" : "loses");
  }
 
  ~Bettor() {
    // need to let subject know we don't exist
    hr->detatch(this);
  }
};

For more details on the code, checkout .\\lectures\\se\\02-observer


Decorator pattern:

Suppose we want to enhance an object at runtime.

(e.g. add or change features). For example. a windowing system - we could start with a basic window, and have options to add a scrollbar and a menu. We would like to choose these options at runtime.

Adding these at runtime means we could start with just a basic window and add a scroller when it becomes necessary for example.

// insert picture here

class Componentdefines the interface: operations your objects will provide. ConcreteComponent is your basic undecorated object, so offers base implementations of these.

The decoratorclasses all inherit from Decoratorwhich inherits from component.


For example: Ordering Pizza

// insert first pizza uml here

class Pizza {
public:
  virtual Float price() const = 0;
  virtual string desc() const = 0;
  virtual ~pizza() {}
};
 
// crust and dsauce
class CrustAndSauce : public Pizza {
public:
  float price() const override {return 5.99;}
  string desc() const override {return "pizza";}
};
 
//

// insert second pizza uml here

Now, we need our decorator

class Decorator : public Pizza {
  Pizza *component;
public:
  Decorator(Pizza *D) : component{p} {}
  virtual ~Decorator() {delete component;}
  float price() const override {
    return component->price();
  }
  string desc() const override {
    return component->desc();
  }
};
 
class Toping: public Decorator {
  string theTopping;
public:
  Toping(Pizza *p, string t): Decorator{p},theTopping{t} {}
  float price() const override {
    return Decorator::price() + 1.29;
  }
 
  string desc() const override {
    return Decorator::desc() + " with " + theTopping;
  }
};
 
// stuffed crust is similar
 
// checm the code in repo