C++ 观察者模式专业化

C++ 观察者模式专业化,c++,observer-pattern,C++,Observer Pattern,我正在尝试使用观察者模式进行一些输入,例如: class Observer { public: virtual void notify(Subject & o)=0; }; class Subject { public: virtual void register(Observer * o)=0; } 我有两个具体的主题鼠标、键盘,以及类特定的函数,我希望具体的观察者调用getkeypress、getmousemotion等 有没有办法在不改变接口或向下转换引用的情况

我正在尝试使用观察者模式进行一些输入,例如:

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

class Subject
{
public:
    virtual void register(Observer * o)=0;
}
我有两个具体的主题鼠标、键盘,以及类特定的函数,我希望具体的观察者调用getkeypress、getmousemotion等


有没有办法在不改变接口或向下转换引用的情况下,在具体的观察者类中专门化notify函数?我尝试过重载函数,但显然这不起作用,因为具体的主体不知道派生的观察者。

通常不会给观察者一个纯虚拟的通知函数。相反,您的主题应该重新实现一个已更改的函数,Observer::notify调用其所有主题。这样,您可以在鼠标和键盘中重新实现以调用所需的函数

这确实需要对所展示的界面进行更改,因为现在它并不完全正确

有没有办法在不改变接口或向下转换引用的情况下,在具体的观察者类中专门化notify函数

我想没有

但是,通过使用作为Observer子类的类模板并使用于实例化类模板的类型完全了解派生类型,可以最大限度地减少使用dynamic_cast的位置

我已经多次有效地使用该模式

解决方案1 这使用了一种模式,其中观察者只能观察一种类型的对象

#include <iostream>

class Subject;

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

class Subject
{
   public:

      // Remove the Observer argument from the public interface.
      // Make the derived class construct the right type of observer
      // and use the registerObserverImpl function to do the work.
      // With this, client code doesn't need to know the kind of Observer
      // a sub-class of Subject uses.
      virtual void registerObserver() = 0;

      void notifyObserver()
      {
         observer->notify(*this);
      }

   protected:

      // Helper function for derived classes.
      void registerObserverImpl(Observer * o)
      {
         observer = o;
      }

   private:
      // The observer.
      Observer* observer;
};

// A class template that is responsible for performing dynamic_cast
// and passing a reference to the derived type to the concrete Observer.
template <typename RealObserver>
class TemplateObserver : public Observer
{
   using ConcreteSubject = typename RealObserver::SubjectType;
   virtual void notify(Subject& o)
   {
      // The only place you need to use dynamic_cast.
      RealObserver().notify(dynamic_cast<ConcreteSubject&>(o));
   }
};

class Mouse : public Subject
{
   public:
      virtual void registerObserver();
};

// The concrete Observer of Mouse. It doesn't need to be derived from
// Observer since TemplateObserver takes care of that.
class MouseObserver
{
   public:
      using SubjectType = Mouse;
      void notify(Mouse& m)
      {
         std::cout << "In MouseObserver::notify\n";
         // Use the Mouse anyway you want.
      }
};

void Mouse::registerObserver()
{
   registerObserverImpl(new TemplateObserver<MouseObserver>());
}

class Keyboard : public Subject
{
   public:
      virtual void registerObserver();
};

// The concrete Observer of Keyboard. It doesn't need to be derived from
// Observer since TemplateObserver takes care of that.
class KeyboardObserver
{
   public:

      using SubjectType = Keyboard;
      void notify(Keyboard& k)
      {
         std::cout << "In KeyboardObserver::notify\n";
         // Use the Keyboard anyway you want.
      }
};

void Keyboard::registerObserver()
{
   registerObserverImpl(new TemplateObserver<KeyboardObserver>());
}

int main()
{
   // Client code does not need to know about MouseObserver or
   // KeyboardObserver.

   Mouse m;
   m.registerObserver();
   m.notifyObserver();

   Keyboard k;
   k.registerObserver();
   k.notifyObserver();
}
解决方案2 这使用了一种模式,观察者可以观察任意数量的对象类型

#include <iostream>

class Subject;

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

class Subject
{
   public:

      // Make the class polymorphic
      virtual ~Subject() {}

      void registerObserver(Observer * o)
      {
         observer = o;
      }
      void notifyObserver()
      {
         observer->notify(*this);
      }

   private:
      // The observer.
      Observer* observer;
};

// A class template that is responsible for performing dynamic_cast
// and passing a reference to the derived type to the concrete Observer.
template <typename RealObserver, typename RealSubject>
class TemplateObserver : public Observer
{
   virtual void notify(Subject& o)
   {
      // The only place you need to use dynamic_cast.
      RealObserver().notify(dynamic_cast<RealSubject&>(o));
   }
};

class Mouse : public Subject
{
};

// The concrete Observer of Mouse. It doesn't need to be derived from
// Observer since TemplateObserver takes care of that.
class MouseObserver
{
   public:
      using SubjectType = Mouse;
      void notify(Mouse& m)
      {
         std::cout << "In MouseObserver::notify\n";
         // Use the Mouse anyway you want.
      }
};

class Keyboard : public Subject
{
};

// The concrete Observer of Keyboard. It doesn't need to be derived from
// Observer since TemplateObserver takes care of that.
class KeyboardObserver
{
   public:

      using SubjectType = Keyboard;
      void notify(Keyboard& k)
      {
         std::cout << "In KeyboardObserver::notify\n";
         // Use the Keyboard anyway you want.
      }
};

class CombinedObserver
{
   public:

      void notify(Mouse& m)
      {
         std::cout << "In CombinedObserver::notify\n";
         // Use the Mouse anyway you want.
      }

      void notify(Keyboard& k)
      {
         std::cout << "In CombinedObserver::notify\n";
         // Use the Keyboard anyway you want.
      }
};

int main()
{
   // Client code does not need to know about MouseObserver or
   // KeyboardObserver.

   Mouse m;
   m.registerObserver(new TemplateObserver<MouseObserver, Mouse>());
   m.notifyObserver();

   Keyboard k;
   k.registerObserver(new TemplateObserver<KeyboardObserver, Keyboard>());
   k.notifyObserver();

   m.registerObserver(new TemplateObserver<CombinedObserver, Mouse>());
   m.notifyObserver();

   k.registerObserver(new TemplateObserver<CombinedObserver, Keyboard>());
   k.notifyObserver();
}

你能详细说明一下吗?嗯,这是一种有趣的方法,但在这种情况下,具体的观察者需要与其他对象交互,一个摄影机类,我不确定这会有多有用。@IanYoung,我想这不会是一个问题,但在我看到相机的外观以及您希望如何使用它作为观察者之前,我无法提出任何具体的建议。@R Sahu这不是我的确切意思,但我可以看到混乱。我的意思是,澄清一下,我有一个moduleobserver,它充当控制器主体(在本例中为键盘或鼠标)和相机之间的中介。中介所做的只是查询鼠标移动和/或按键,然后调用相机上的移动/旋转函数。@IanYoung,听起来很合理。您可以创建两个观察者,而不是一个-一个用于键盘,一个用于鼠标。它们一起可以执行模块的角色。在我的建议中,使模块同时成为键盘和鼠标的观察者将是一个问题。@R Sahu到目前为止听起来不错,但我发现自己回到了最初的问题,即当观察者->通知*这一点时;通过键盘或鼠标调用。我仍然需要将每个主题向下转换到键盘或鼠标对象,以便查询它们,我正试图避免这种向下转换。