QuantLib: Observer and Observable

The observer design pattern is a classic design pattern that lets objects, called observables, notify a number of dependant objects, called observers, when a change of the observable’s state occurs. This is often used in distributed event handling systems and is also the foundation of the MVC architecture. QuantLib implements the observer design pattern with the two classes Observer and Observable.

The Observer class allows observables to be registered and unregistered using the following methods.

std::pair<
  std::set<boost::shared_ptr<Observable> >::iterator,
  bool
>
registerWith(const boost::shared_ptr<Observable>&);
Size unregisterWith(const boost::shared_ptr<Observable>&);
void unregisterWithAll();

The registerWith() method will register the observer with a particular observable. This means that the observer will be notified when the observable changes. The method unregisterWith() will remove the observer from the list of objects to be notified when the observable changes, and unregisterWithAll() will unregister the observer with all observables that it might be registered with.

virtual void update() = 0;

When the observable changes it will cause the update function of all its registered observers to be called. This function should be implemented by a concrete observer class to take whatever actions should be taken when the observable changes.

Observable

The Observable class defines one central method.

void notifyObservers();

This method will notify all the registered observers of changes in the observable and cause the update() function of each of these to be called. A concrete observable class should call the notifyObservers() function whenever its internal state has changed.

Example

Let’s look at an example to understand exactly how the observer pattern works.

class MyObservable : public Observable {
  private:
    Real amount;
    Natural year;
  public:
    void calculateInterest(Real amount, Real rate, Natural years) {
      this->amount = amount;
      this->year = 0;
      notifyObservers();
      for (this->year=1; this->year<=years; ++this->year) {
        this->amount = this->amount*(1.0 + rate);
        notifyObservers();
      }
    }
    Real getAmount() {
      return this->amount;
    }
};

typedef boost::shared_ptr<MyObservable> pMyObservable;

class MyObserver : public Observer {
  private:
    pMyObservable observable;
    std::string name;
  public:
    MyObserver(pMyObservable obs, std::string n)
        : observable(obs), name(n)
    {
      this->registerWith(observable);
    }
    MyObserver(const MyObserver &observer)
      : Observer(observer),
        observable(observer.observable),
        name("Copy of "+observer.name)
    {}
    void update() {
      std::cout << name << " "
                << observable->getAmount() << std::endl;
    }
};

int main() {
  pMyObservable calc(new MyObservable);
  MyObserver observerA(calc, "observer a");
  MyObserver observerB(calc, "observer b");

  std::cout << "First calculation" << std::endl;
  calc->calculateInterest(100.0, 0.03, 5);

  observerB.unregisterWith(calc);

  std::cout << "Second calculation" << std::endl;
  calc->calculateInterest(100.0, 0.04, 5);

  return 0;
}

The MyObservable class defines a method called calculateInterest() which will add the interest onto a given amount for a set number of years. Every time that the amount is updated, the observable calls the notifyObservers() method which is implemented in the Observable class. This in turn will call the update method of every observer. The main() function creates an object of type MyObservable named calc. It then creates two observers which register themselves with the observable in their constructor. After this the calculateInterest() method is called. During this first calculation both observers are registered with the observable and, thus,both will print out the accrued amount after every year. After this calculation the second observer is un-registered with the observable after which a second calculation is carried out. This time only the first observer reacts to the changes. The output of the example is written out below.

observer a 100
observer b 100
observer a 103
observer b 103
observer a 106.09
observer b 106.09
observer a 109.273
observer b 109.273
observer a 112.551
observer b 112.551
observer a 115.927
observer b 115.927
Second calculation
observer a 100
observer a 104
observer a 108.16
observer a 112.486
observer a 116.986
observer a 121.665

Copying Observers and Observables

The behavior of the Observer and the Observable classes with respect to copying might be slightly unexpected. When copying one observer to another, either by the copy constructor or by the assignment operator, then the new observer will also be registered with all the observables which the original observer was registered with. On the other hand, when copying an observable the observable so created or modified will not notify the observers of the assigning object. When the value is changed with the assignment operator then the object which is changed will notify its observers. The philosophy behind this is that a newly created observable is not the one that the observer has registered with.

To illustrate we extend the main() function in the above example.

pMyObservable calc2(new MyObservable(calc));

std::cout << "Third calculation" << std::endl;
// The following line will not produce any outpt
calc2->calculateInterest();

MyObserver observerC(observerA);

std::cout << "Fourth calculation" << std::endl;
// The following calculation will notify observerA and observerC
calc->calculateInterest();

The calculations carried out by calc2 will not notify any observer because the calc2 observable has not copied the observers from the calculate object. The last calculation carried out by the original calculate object, on the other hand will notify both the observerA and observerC because observers automatically register with the observables when copied.

2 Comments

  1. Azerty

    There’s a problem in “observable(observer.observable)”, because “observable” is meant to be private in MyObserver…

    Reply
  2. Azerty

    Please do not take into account my previous comment…

    Reply

Leave a Comment

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>