【问题标题】:Observer pattern specialisation观察者模式专业化
【发布时间】:2023-03-06 09:04:01
【问题描述】:

我正在尝试将观察者模式用于一些输入内容,例如:

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

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

我有两个具体的主题(鼠标、键盘),以及我希望具体观察者调用的类特定函数(getkeypress、getmousemotion 等)。

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

【问题讨论】:

    标签: c++ observer-pattern


    【解决方案1】:

    有没有办法在具体的观察者类中专门化通知函数而不改变接口或向下转换引用?

    我认为没有。

    但是,您可以通过使用作为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();
    }
    

    输出

    In MouseObserver::notify
    In KeyboardObserver::notify
    

    解决方案 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();
    }
    

    输出

    In MouseObserver::notify
    In KeyboardObserver::notify
    In CombinedObserver::notify
    In CombinedObserver::notify
    

    【讨论】:

    • 嗯,一个有趣的方法,但由于具体的观察者需要与其他对象(在本例中为相机类)交互,我不确定这会有多大用处。
    • @IanYoung,我怀疑这不会是一个问题,但我无法提出任何具体的建议,直到我看到 Camera 的样子以及您希望如何将其用作观察者。
    • @R Sahu 这不是我的意思,但我可以看到混乱。澄清一下,我的意思是我有一个模块(观察者),它充当控制器(主体,在本例中为键盘或鼠标)和相机之间的中介。中介所做的只是查询鼠标移动和/或按键,然后调用相机上的移动/旋转函数。
    • @IanYoung,听起来很合理。您可以创建两个观察者而不是一个——一个用于键盘,一个用于鼠标。他们可以一起扮演module 的角色。在我的建议中,让module 成为键盘和鼠标的观察者将是一个问题。
    • @R Sahu 到目前为止听起来不错,但我发现自己又回到了最初的问题,即当observer-&gt;notify(*this); 被键盘或鼠标调用时。为了查询它们,我仍然需要将每个主题向下转换为 KeyboardMouse 对象,而我正试图避免这种向下转换。
    【解决方案2】:

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

    这确实需要更改您的界面,因为现在还不太正确。

    【讨论】:

    • 您能详细说明一下吗?
    猜你喜欢
    • 2016-02-20
    • 2023-04-10
    • 1970-01-01
    • 2022-12-14
    • 1970-01-01
    • 1970-01-01
    • 2021-11-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多