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
Observableconstructor 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);withchannelthe 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
}
};
