1

I'm working on implementing an observer design pattern with a notification object that I can change to suit various observed classes.

Here is the observer framework:

notify.h:

class INotification //Notification container
{
    public:
        virtual ~INotification()=0;
};

inline INotification::~INotification() {}

class IObserver
{
    public:
        virtual ~IObserver();
        virtual void update(INotification*)=0;
};

inline IObserver::~IObserver() {}

class ISubject
{
    public:
        virtual ~ISubject();
        virtual void attach(IObserver*)=0;
        virtual void detach(IObserver*)=0;
        virtual void notify()=0; //Note: observer deletes notifications
};

inline ISubject::~ISubject() {}

I am implementing a timer class that I want other classes to observe for timer events:

timer.h:

class ITimerObserver;

class ITimer : public ISubject
{
    public:
        virtual ~ITimer();
        virtual void setInterval(const unsigned int,const unsigned int)=0; //Seconds, Microseconds
        virtual void run()=0; //Check for triggering
        virtual const timeval& now()=0;
        virtual bool isItTime(const timeval&,const timeval&)=0;
};

inline ITimer::~ITimer() {}

class CTimer : public ITimer
{
    protected:
        std::vector<IObserver*> observers;
        timeval interval; //How often we are triggering
        timeval lastTrigger; //When we were last triggered
        timeval current; //Our current time
    private:
        virtual ~CTimer();
        virtual void attach(IObserver*);
        virtual void detach(IObserver*);
        virtual void notify();
        virtual void setInterval(const unsigned int,const unsigned int); //Seconds, Microseconds
        virtual void run(); //Check for triggering
        virtual const timeval& now();
        virtual bool isItTime(const timeval&,const timeval&);
};

class ITimerNotification : public INotification
{
    public:
        virtual ~ITimerNotification();
        virtual const timeval& getTime()=0;
};

inline ITimerNotification::~ITimerNotification() {}

class CTimerNotification : public ITimerNotification
{
    public:
        CTimerNotification(const timeval& t)
        {
            time = t;
        }
    protected:
        timeval time;
    private:
        virtual ~CTimerNotification();
        virtual const timeval& getTime()
        {
            return time;
        }
};

class ITimerObserver : public IObserver
{
    public:
        virtual void update(ITimerNotification*)=0;
};

So I want to be able to pass a more specific Notification object (A TimerNotification) whenever a timer event happens, so that I can call a specific update() function on the observer, so I made a new Observer class (ITimerObserver).

Here is the function that notifies the observer on a timer event:

void CTimer::notify()
{
    std::vector<IObserver*>::iterator it;
    for(it=observers.begin();it!=observers.end();++it)
    {
        ITimerNotification* notification = new CTimerNotification(now());
        (*it)->update(notification);
    }
}

Here is the actual observer itself:

class TestObserver : public ITimerObserver
{
    public:
        virtual void update(INotification* note)
        {
            std::cout<<"???: TestObserver: update()!\n";
        }
        virtual void update(ITimerNotification* note)
        {
            std::cout<< note->getTime().tv_sec << "." << note->getTime().tv_usec <<": TestObserver: update()!\n";
        }
};

When run, the program runs the interface method, void update(INotification) instead of the more specific ITimerNotification as I would expect. The trouble is, how do I get the CTimer class to know about the TimerObserver without breaking the interface contract that says it only takes a Base Observer pointer?

Brian
  • 145
  • 1
  • 3
  • 13

1 Answers1

1

To answer the first part of the question:

 ITimerNotification* notification = new CTimerNotification(now());
 (*it)->update(notification);

This code passes notification to an IObserver::update method, of which there is only one:

virtual void update(INotification*)=0;

Hence the call to that virtual method in TestObserver.

For the second part, you need to recognise that you wish the call to in a sense be virtual on two types, the observer and notification. This is known as double dispatch, and requires some work in C++.

The key point to understand is the static and run-time binding of the function calls. At the calling point, for example:

(*it)->update(notification);

the compiler can only do static resolution of the names of functions. Where these are virtual calls, there will be run-time binding to the actual method based on the type of object the method is being called on (not the parameter). So in order to do double dispatch through built-in mechanisms, you need to call a virtual method on both the notification and the observer.

See for example:

Multiple dispatch in C++

How does double dispatch work in Visitor pattern?

This topic is also covered in great detail in one of the Meyer's books (I forget which.)

Community
  • 1
  • 1
Keith
  • 6,756
  • 19
  • 23
  • I will look into combining a visitor pattern into my design. Is this design flawed? C++ doesn't seem to support multiple dispatch natively. – Brian Dec 07 '11 at 05:11
  • Didn't mean to imply you need the visitor pattern per so; the observer pattern is very similar. As to whether the design is OK depends on whether you really need to know all the derived types to define the handlers. If so, go ahead and implement double dispatch. – Keith Dec 07 '11 at 05:15
  • Ah I've run into a stack overflow due to infinite recursion. It seems the observer's appropriate update() method still isn't being called correctly... – Brian Dec 07 '11 at 05:51