Problems implementing the "Observer" pattern


Question

I have met an interesting problem while implementing the Observer pattern with C++ and STL. Consider this classic example:

class Observer {
public:
   virtual void notify() = 0;
};

class Subject {
public:
   void addObserver( Observer* );
   void remObserver( Observer* );
private:
   void notifyAll();
};

void Subject::notifyAll() {
   for (all registered observers) { observer->notify(); }
}

This example can be found in every book on design patterns. Unfortunately, real-life systems are more complex, so here is the first problem: some observers decide to add other observers to the Subject on being notified. This invalidates the "for" loop and all the iterators, that I use. The solution is rather easy - I make a snapshot of the registered observers list and iterate over the snapshot. Adding new observers does not invalidate the snapshot, so everything seems ok. But here comes another problem: observers decide to destroy themselves on being notified. Even worse, one single observer can decide to destroy all other observers (they are controlled from the scripts), and that invalidates both the queue and a snapshot. I find myself iterating over de-allocated pointers.

My question is how should I handle the situations, when observers kill each other? Are there any ready-to-use patterns? I always thought that "Observer" is the easiest design pattern in the world, but now it seems it is not that easy to implement it correctly...

Thank you, everyone for your interest. Let us have a decisions summary:

[1] "Don't do it" Sorry, but it is a must. Observers are controlled from the scripts and are garbage-collected. I cannot control the garbage collection to prevent their de-allocation;

[2] "Use boost::signal" The most promising decision, but I cannot introduce boost on the project, such decisions must be made by the project leader only (we are writing under Playstation);

[3] "Use shared__ptr" That will prevent observers from de-allocation. Some sub-systems may rely on memory pool cleanup, so I don't think I can use shared_ptr.

[4] "Postpone observer deallocation" Queue observers for removal while notifying, then use the second cycle to delete them. Unfortunately, I cannot prevent the deallocation, so I use a trick of wrapping observer with some kind of "adaptor", keeping actually the list of "adaptors". On destructor, observers unassign from their adaptors, then I take my second cycle to destroy empty adaptors.

p.s. is it ok, that I edit my question to summarize all the post? I am noob on StackOverflow...

1
32
6/19/2009 3:28:55 PM

Accepted Answer

Very interesting issue.

Try this:

  1. Change remObserver to null out the entry, rather than just removing it (and invalidating the list iterators).
  2. Change your notifyAll loop to be:

    for (all registered observers) { if (observer) observer->notify(); }

  3. Add another loop at the end of notifyAll to remove all null entries from your observer list

14
6/19/2009 4:18:40 PM

Personally, I use boost::signals to implement my observers; I'll have to check, but I believe it handles the above scenarios (edited: found it, see "When can disconnections occur"). It simplifies your implementation, and it doesn't rely on creating custom class:

class Subject {
public:
   boost::signals::connection addObserver( const boost::function<void ()>& func )
   { return sig.connect(func); }

private:
   boost::signal<void ()> sig;

   void notifyAll() { sig(); }
};

void some_func() { /* impl */ }

int main() {
   Subject foo;
   boost::signals::connection c = foo.addObserver(boost::bind(&some_func));

   c.disconnect(); // remove yourself.
}

Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Icon