What would happen if Andre Alexandrescu, the author of Modern C++ Design and Martin Fowler,
the author of Refactoring were required to refactor and generalize the Observer pattern sample code from the book
Design Patterns?
I imagine we'd end up with a policy-based observer pattern.
I noticed that at my company, somebody implemented the Observer pattern directly from the sample code in the book,
with the custom-made container and all. When I looked at it, I realized that the Observer sample implementation is
helplessly out-of-date with modern C++ design. Turn to page 301 of Design Patterns, and you'll see the original sample code:
class Subject {
public:
virtual ~Subject();
virtual void Attach(Observer*);
virtual void Detach(Observer*);
virtual void Notify();
protected:
Subject();
private:
List *_observers;
};
class Observer {
public:
virtual ~Observer();
virtual void Update(Subject *theChangedSubject) = 0;
protected:
Observer();
};
The idea is that you'll have one descendent class inherit from Subject (say, "Data"), and another class inherit from Observer (say, "Window"). Then your Window can Attach()
itself to the Subject. And when the Subject changes, it'll call
Notify(), invoking
Update() on all the Observers that had
Attached to it.
Here are some issues:
- The custom-made container, List. Better now to use the standard template library containers and algorithms.
- The code is pull-model. The subject passes only a pointer to itself. Thus, the observer has to have programmed into it knowledge of what the singular Update() method call really means. The observer may have to query the subject for info on how to respond. (Even worse, it'd have to downcast the observer to the concrete descendent class to find a method to query. So you'd better turn on RTTI.)
- "Implementation Issue 6: Dangling pointers to deleted observers." (Design Patterns p. 297) If the observers are deleted before the subject, the subject must be notified to remove the pointers it has to the observers.
Motivating Examples: Here are some potential clients, and how they'd benefit from a refactored Observer pattern:
- A new C++ operating system. We'd like a push-model type-safe messaging system. UserMode objects may subscribe only to the MouseMessages, NetworkMessages, KeyboardMessages, etc. that they want. Imagine never having to opaquely cast LPARAMs and WPARAMs again. All messages will by typesafe and contain only relevant state!
- The Sims games. Objects in the Sims broadcast to their local surroundings their useful aspects. Interested Sims may react to the broadcast as they please. For example, a Refrigerator object might broadcast that is has nice cold ice cream for any hungry Sims, and a hot tub object might broadcast that it has soothing hot water for any tired Sims.
- A Non-Linear Media Editor. This would have multiple views into multiple data sources. Maybe we'd have a timeline view, or an album view, or a 3D wireframe view, or a peak-meter view, and it depends on dynamic data.
So, let's pretend to be Andrei and Martin and refactor! Into the breach!