【问题标题】:C++ Own Observer PatternC++ 自己的观察者模式
【发布时间】:2012-08-19 18:35:05
【问题描述】:

我正在设计一个应该以这种方式工作的观察者模式:观察者调用EventDispatcherAddEventListener 方法并传递一个字符串,该字符串是event 的名称、PointerToItselfPointerToItsMemberMethod

之后event 发生在EventDispatcher 内部;它会查看订阅列表,如果有的话,分配给这个事件的会调用observeraction 方法。

我来到了这个EventDispatcher.h注意包含一些伪代码。

有两个问题:

  1. 如何在struct Subscription中定义action的类型?
  2. 我走对了吗?

PS:不,我不会使用boost任何其他库。

#pragma once

#include <vector>
#include <string>

using namespace std;

struct Subscription
{
        void*                   observer;
        string                  event;
        /*  u_u  */             action;
};

class EventDispatcher
{
    private:
        vector<Subscription>    subscriptions;

    protected:
        void                    DispatchEvent ( string event );

    public:
        void                    AddEventListener ( Observer* observer , string event , /*  u_u  */ action );
        void                    RemoveEventListener ( Observer* observer , string event , /*  u_u  */ action );
};

这个头在EventDispatcher.cpp中是这样实现的

#include "EventDispatcher.h"

void    EventDispatcher::DispatchEvent ( string event )
{
    int key = 0;
    while ( key < this->subscriptions.size() )
    {
        Subscription subscription = this->subscriptions[key];
        if ( subscription.event == event )
        {
            subscription.observer->subscription.action;
        };
    };
};

void    EventDispatcher::AddEventListener ( Observer* observer , string event , /* */ action )
{
    Subscription subscription = { observer , event , action );
    this->subscriptions.push_back ( subscription );
};

void    EventDispatcher::RemoveEventListener ( Observer* observer , string event , /* */ action )
{
    int key = 0;
    while ( key < this->subscriptions.size() )
    {
        Subscription subscription = this->subscriptions[key];
        if ( subscription.observer == observer && subscription.event == event && subscription.action == action )
        {
            this->subscriptions.erase ( this->subscriptions.begin() + key );
        };
    };
};

【问题讨论】:

  • 不幸的是,您没有使用 boost,因为这将允许一个简单且类型安全的解决方案,该解决方案将优于您当前的方法并且更加灵活。是否允许使用 C++11 解决方案?
  • 我还不知道,C++11 是什么……这是一个新标准,对吧?我想知道,如果我的g++ 已经知道了吗?新标准可以用,不是库……

标签: c++ pointers function-pointers observer-pattern observers


【解决方案1】:

您可以定义一个 Action 类或传递一个 lambda 函数 (C++11)。在后一种情况下,动作可以定义为

function<void (EventDispatcher*)> action;

你会像下面这样注册观察者

Observer * me = this;
observable->AddEventListener (this, "EventName", [me] (EventDispatcher* dispatcher) {
   // code here; me is available
});

您可能应该使用智能弱指针将 Observers 存储在 EventDispatcher 中,这样您就不必担心取消注册。

编辑:添加了以下示例(可能只有一个订阅,但应该说明这个想法 - 您必须小心不要引用不再存在的对象)

struct Observable {
   std::weak_ptr<function<void (const Observable&)>> action;

   void AddEventListener (std::weak_ptr<function<void (const Observable&)>> theAction) {
      action = theAction;
   }

   void EventRaised () {
      if (!action.expired ()) {
         auto theAction = action.lock ();
         (*theAction) (*this);
      }
   }
};

struct Observer {
...
   void CallOnEvent (const Observable & observable) {
      // do something
   }

   // field to store the action as long as it is needed
   std::shared_ptr<function<void (const Observable&)>> action;

   void ... {
      auto me = this;
      action = std::make_shared<function<void (const Observable&)>> (
         [me] (const Observable& observable) {
             me->CallOnEvent (observable);
         }
      );
      // we could have as well used std::bind
      observable.AddEventListener (action);
   }
};

【讨论】:

  • 我可以将一个指向观察者成员函数的指针传递给AddEventListener 并从EventDispatcher 调用它吗?我该怎么做?谢谢?
  • @Kolyunya 只需将 std::bind(Observer::whateverMethod, me) 作为函数而不是 lambda 传递。
  • 我只是没有得到你的代码...它一定是新标准 c++11,我还不知道...谢谢和抱歉...
【解决方案2】:

也许您应该只创建一个由“用户”派生的类:

class Action {
   public:
      friend class EventDispatcher;

      virtual SomeResultType DoThis() = 0;

   private:
      /* Some common data */
};

只需将一些派生自类动作类型的变量传递给 AddEventListener。触发对应事件时,填写常用数据,调用DoThis()方法即可。

void EventDispatcher::DispatchEvent ( string event )
{
    int key = 0;
    while ( key < this->subscriptions.size() )
    {
        Subscription subscription = this->subscriptions[key];
        if ( subscription.event == event )
        {
            subscription->action();
        };
    };
};

对于 AddEventListener:

void EventDispatcher::AddEventListener ( Observer* observer , string event , Action* action )
{
    Subscription subscription = { observer , event , action );
    this->subscriptions.push_back ( subscription );
};

Action 派生类的示例:

class myAction: public Action {
   public:
      // Implement the DoThis() method
      void SomeResultType DoThis() {
          cout << "Hello World!";
          return SomeValue;
      }
};

// To use the action,
myAction* act = new myAction;
myEventDispatcher.AddEventListener(someObserver, "HelloWorld", act);

这是实现动作(和回调)的最安全方式之一。

【讨论】:

  • 我可以将一个指向observer 的成员函数的指针传递给AddEventListener 并从EventDispatcher 调用它吗?我该怎么做?谢谢?
  • 抱歉耽搁了。请避免使用函数指针。在您的 DispatchEvent() 方法中,您可以只使用 action->DoThis();
  • 对不起,我不明白...假设观察者有一个非静态方法DoSmth()。如何将此方法传递给EventDispatcherEventDispatcher 稍后将如何调用此方法?
【解决方案3】:

在其最简单的形式中,u_u 可以是一个函数指针,例如

typedef void (*u_u)(void*); // or whatever arguments u like

那么您只需提供一个在触发事件时调用的函数。

void myaction(void* arg)
{
  ...
}

Subscription s;
...
s.action = myaction;

【讨论】:

    猜你喜欢
    • 2021-11-22
    • 2013-12-30
    • 2016-02-20
    • 2023-04-10
    • 1970-01-01
    • 2012-02-15
    • 2011-05-15
    • 2013-10-28
    • 1970-01-01
    相关资源
    最近更新 更多