wiki:ObserverHowto

Version 2 (modified by ankele, 12 years ago) ( diff )

Observable's constructor missed channel creation

Observer Howto

Theory

The Observer pattern is a way of implementing a one-to-many dependency. There usually is one observable object automatically notifying multiple observer objects of its changes. For doing so the observer keeps a list of its observables. The observers in turn have to sign on to the observable's list.

Implementation

Creating an observable

  • derive the observable class from Observable
  • create an enum type with one element for each type of change about to occur
  • in the constructor call the Observable constructor giving a string name for your class
  • also in the constructor create the channels (see code below)
  • surround any code that makes notify-worthy changes with OBSERVE; and NOTIFY(channel); with channel the appropriate enum constant

For example:

  class MyObservable : public Observable
  {
  public:
    enum NotificationType{ // should be public (the observer will want to use this type!)
      SomethingDone,
      OtherStuffDone,
      NotificationType_MAX
    };
    MyObservable() : Observable("MyObservable")
    {
      Channels *OurChannel = new Channels;
      NotificationChannels.insert( std::make_pair(this, OurChannel) );
      // add instance for each notification type
      for (size_t type = 0; type < NotificationType_MAX; ++type)
        OurChannel->addChannel(type);
      // other constructor stuff
      ...
    }
    ....
    void doSomeChanges()
    {
      OBSERVE;
      ... // do the actual changes
      NOTIFY(SomethingDone); // send notifications to all observers
    }
  };

Creating an observer

  • derive the observer class from Observer
  • add a virtual member function

virtual void MyObserver::update(Observable *publisher);

to be called on changes

  • (optionally) add a virtual member function

virtual void MyObserver::recieveNotification(Observable *publisher, Notification_ptr notification);

to be called on changes with more information

  • add a virtual member function

virtual void MyObserver::subjectKilled(Observable *publisher);

to be called when the observable is deleted

  • signon...

Simple example:

  class MyObserver : public Observer
  {
  public:
    MyObserver()
    {
      MyObservable *observable = ...;
      observable->signOn(this);
    }
    virtual ~MyObserver()
    {
      observable->signOff(this);
    }
    virtual void update(Observable *publisher)
    {
      // react to changes
    }
    virtual void subjectKilled(Observable *publisher){}
  };

More complex example:

  class MyObserver : public Observer
  {
  public:
    MyObserver()
    {
      MyObservable *observable1 = ...;
      MyObservable *observable2 = ...;
      observable1->signOn(this); // receive all notifications
      observable2->signOn(this, MyObservable::SomethingChanged); select specific notifications
      observable2->signOn(this, MyObservable::OtherStuffChanged);
    }
    virtual ~MyObserver()
    {
      observable1->signOff(this);
      observable2->signOff(this);
    }
    virtual void update(Observable *publisher)
    {
      // react to any changes
    }
    virtual void recieveNotification(Observable *publisher, Notification_ptr notification)
    {
      if (notification->getChannelNo() == MyObservable::SomethingChanged){
        // react to specific notification
      }
    }
    virtual void subjectKilled(Observable *publisher){}
  };

Caveats

Using observableObject->signOff(this); in the observer's destructor will cause a segfault in case the observable is deleted while the observer lives on.

For rather short lived observables this has to be circumvented:

  class MyObserver : public Observer
  {
    MyObservable *observable;

    MyObserver()
    {
      observable = ...;
      observable->signOn(this);
    }
    virtual ~MyObserver()
    {
      if (observable) // only sign off if the observer still lives
        observable->signOff(this);
    }
    virtual void update(Observable *publisher)
    {
      // react to changes
    }
    virtual void subjectKilled(Observable *publisher)
    {
      observable = NULL; // don't sign off
    }
  };
Note: See TracWiki for help on using the wiki.