Re: Observer Design Pattern



On 2006-05-31 06:00:00 -0500, "Krivenok Dmitry" <dima@xxxxxxxxxxxxxxxxxx> said:

Hello All!

I am trying to implement my own Design Patterns Library.

If you are talking about a code library (as opposed to a library of documents) then I'd like to discourage you. Design Patterns are not the kind of thing that you can turn into a reusable coding library.

Observer is one of the exceptions to this rule; but it's only a partial exception for the reasons that you have already pointed out in your post.

I have included some comments below..


Consider the following pseudo-code example:

class Subject
{
public:
virtual void Attach(Observer*) = 0;
virtual void Detach(Observer*) = 0;
virtual void Notify() = 0;

virtual @@@ GetState() = 0; // !!! What type should be
returned???
};

This is the problem with making Observer part of a reusable library. There is no good option for GetState. I usually omit it entirely. See below.


class Observer
{
public:
virtual void Update(Subject*) = 0;
};

I usually don't pass the Subject* to the Observer. I like my observers to attach to one, and only one, subject. This is just my particular style and I wouldn't make it a hard and fast rule. But read on.


class MySubject : public Subject
{
public:
void Attach(Observer*)
{
/// Add pair
}
void Detach(Observer*)
{
/// Delete pair
}
void Notify()
{
/// Call observer->Update(this) for each observer in mapping
}

@@@ GetState()
{
/// Return MySubject-specific data. BUT HOW???
}

Again, this is not usually feasible in statically typed languages like C++. You could pass a void* and then reinterpret_cast it; but that's hideous. Read on.

private:
// Subject-Observer mapping
// MySubject-specific data
};

class YourSubject : public Subject
{
public:
// Along similar lines
private:
// Subject-Observer mapping
// YourSubject-specific data
};

class MyObserver : public Observer /// This class represents MySubject
and YourSubject
{
public:
void Update(Subject* s)
{
/// Only Subject's interface is available here (not MySubject
or YourSubject)
/// I know one bad solution - type switch via dynamic_cast.
if TypeOf(s) == MySubject
{
/// Update MySubject-specific part of representation
}
else if TypeOf(s) == YourSubject
{
/// Update YourSubject-specific part of representation
}

I don't want my observers to have to switch on types. Each of my observers knows the type of the object it is observing. Indeed, since I only have one observer per subject, my observers know exactly which object they are observing.

I would be willing to relax the later rule, but not the former. Even if my observers observed more than one object at a time; I would make sure that all those subjects were of the same type. Therefore I could safely downcast the Subject* to MySubject*.

}
private:
/// MyObserver-specific data (e.g. GUI widgets for subjects
representation)
};


The problem lies in poor Subject's interface.
Subject doesn't know anything about MySubject's private data.

Yes, that is the problem; and there is no general solution for it. You can use templates to create a Subject<MyData> and Observer<MyData> but I find this to be overkill. The simple rule of one-subject/one-observer solves most of these problems.


I don't understand why GoF includes GetState method in Subject's
interface.

That was just an illustration, not a mandatory function. In a "pull-model" observer every subject will have some function(s) that allow the observer to get the state.


Main question:
How to avoid type switching in ConcreteObserver::Update(Subject* s)?

One-subject/one-observer.

Thanks!

P.S.
Sorry for my english :)

Your english was fine.


--
Robert C. Martin (Uncle Bob)  | email: unclebob@xxxxxxxxxxxxxxxx
Object Mentor Inc.            | blog:  www.butunclebob.com
The Agile Transition Experts  | web:   www.objectmentor.com
800-338-6716                  |



.



Relevant Pages

  • Re: MVC in C++
    ... No Observer can handle ... > struct Observable ... > void notifyObservers() ... > string version; ...
    (comp.object)
  • Re: Model-View-Controller
    ... Observer(Observable* model); ... void attach; ... virtual string getCredits() const = 0; ...
    (alt.comp.lang.learn.c-cpp)
  • Re: Observer Design Pattern
    ... I am trying to implement my own Design Patterns Library. ... class Observer ... void attach(Observer* o) { ... MyObserver(const MyObserver&); ...
    (comp.object)
  • Re: Decentralized design
    ... > For a Cellular phone, it is very natural that it's a Telephone of physical ... Buttons, Dialer, CellularRadio, DialerDiaplay and others. ... OBSERVER Pattern Code for ClockTimer ... void Subject::Attach { ...
    (comp.object)