Проблема контрактного дизайна интерфейса шаблона проектирования наблюдателя - PullRequest
1 голос
/ 07 декабря 2011

Я работаю над реализацией шаблона проектирования наблюдателя с объектом уведомления, который я могу изменить для соответствия различным наблюдаемым классам.

Вот структура наблюдателя:

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() {}

Я реализую класс таймера, который я хочу, чтобы другие классы наблюдали за событиями таймера:

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;
};

Поэтому я хочу иметь возможностьдля передачи более конкретного объекта уведомления (A TimerNotification) всякий раз, когда происходит событие таймера, чтобы я мог вызвать определенную функцию update () для наблюдателя, поэтому я создал новый класс Observer (ITimerObserver).

Здесьэто функция, которая уведомляет наблюдателя о событии таймера:

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

Вот сам фактический наблюдатель:

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";
        }
};

При запуске программа запускает метод интерфейса, void update (INotification) вместо более конкретного ITimerNotification, как я ожидал.Проблема в том, как мне узнать класс CTimer о TimerObserver, не нарушая контракт интерфейса, который говорит, что он принимает только указатель Base Observer?

1 Ответ

1 голос
/ 07 декабря 2011

Чтобы ответить на первую часть вопроса:

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

Этот код передает notification методу IObserver::update, из которых только один:

virtual void update(INotification*)=0;

Следовательно, вызов этого виртуального метода в TestObserver.

Во второй части вам необходимо признать, что вы хотите, чтобы вызов в некотором смысле был виртуальным для двух типов, наблюдатель и уведомление .Это известно как двойная диспетчеризация и требует некоторой работы на C ++.

Ключевым моментом, который необходимо понять, является статическая привязка и вызовы функций во время выполнения.В вызывающей точке, например:

(*it)->update(notification);

, компилятор может выполнять только статическое разрешение имен функций.Там, где это виртуальные вызовы, во время выполнения будет выполняться привязка к фактическому методу на основе типа объекта, к которому вызывается метод (а не параметра).Таким образом, чтобы выполнить двойную диспетчеризацию через встроенные механизмы, вам нужно вызвать виртуальный метод как для уведомления, так и для наблюдателя.

См., Например:

Многократная диспетчеризация вC ++

Как работает двойная диспетчеризация в шаблоне Visitor?

Эта тема также подробно рассмотрена в одной из книг Мейера (я забыл, какая.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...