Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Problems implementing the "Observer" pattern

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...

like image 625
SadSido Avatar asked Jun 19 '09 14:06

SadSido


People also ask

What problems can the observer design pattern solve?

The Observer Design Pattern Solves Problems Like: How can a one-to-many dependency between objects be defined without making the objects tightly coupled? How to create a model to update all dependent objects when the state of one object changes?

What is the specific problem in software development that is addressed by implementing the Observer pattern or by using aspect oriented programming?

The problem that the Observer pattern solves is how to maintain con- sistency among several objects that depends on a model data in a way that promotes reuse, keeping a low coupling among classes. In this pattern, every time the Subject's state changes, all the Observers are notified.

When should the Observer pattern be used?

Observer pattern is used when there is one-to-many relationship between objects such as if one object is modified, its depenedent objects are to be notified automatically. Observer pattern falls under behavioral pattern category.


2 Answers

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

like image 100
T.E.D. Avatar answered Sep 25 '22 03:09

T.E.D.


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. } 
like image 32
Todd Gardner Avatar answered Sep 25 '22 03:09

Todd Gardner