Friday 24 June 2011

Two implementations of Observer pattern

Change in some object (publisher, notifier, subject) very often needs to be reflected in another one (subscriber, observer). Publisher can have multiple subscribers, and can notify them on different changes (events, notifications). Subscribers implement handlers for each event they are interested in.

Publisher can have references to all subscribers and through them call event handlers but this makes these objects tightly coupled. Publisher's code would need to be changed in order to add a new subscriber and remove existing one; if some subscriber wants to use different event handler or to change the name of the existing one, publisher's code must be altered. We want to avoid this. Actual publisher must not know about actual subscriber.

Observer pattern decouples publisher and subscriber by introducing an abstract layer through which they talk. Frequently cited solution (which is the one presented in "Design Patterns: Elements of Reusable Object-Oriented Software") still does not achieve full object decoupling: actual subscriber maintains a reference to actual publisher. Furthermore, it does not cover model when publisher passes notification to subscriber.

Following solution overcomes these problems. It defines CEvent class which represents notification (with its unique ID and data). Publisher creates event and passes is to subscribers. Publisher (not any of subscribers) is responsible for CEvent memory cleanup.



Output:

CActualObserver::OnEventIntValChanged(): new int value: 123
CActualObserver::OnEventTextChanged(): new Text value: test1
CActualObserver::OnEventTextChanged(): new Text value: test1
CActualObserver::OnEventTextChanged(): new Text value: test2
CActualObserver::OnEventTextChanged(): new Text value: test2


In this implementation a single class - CNotifier - serves any type of event but the price we needed to pay for this is the lack of type safety: void* casts should be avoided!

If we don't want to use CEvent (with that awful void* member), but still want to have generic solution, we can make CObserver and CNotifier templated on event type:



Output:

CActualObserver::OnEventIntValChanged(): new int value: 123
CActualObserver::OnEventTextChanged(): new Text value: test1
CActualObserver::OnEventTextChanged(): new Text value: test1
CActualObserver::OnEventTextChanged(): new Text value: test2
CActualObserver::OnEventTextChanged(): new Text value: test2

No comments: