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;
andNOTIFY(channel);
withchannel
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 } };