C++中的通用观察者模式

Generic observer pattern in C++

本文关键字:观察者模式 C++      更新时间:2023-10-16

在我的应用程序中的许多情况下,我需要类A将自己注册为类B上的侦听器,以便在发生某些事情时接收通知。在每种情况下,我都定义了一个单独的接口B实现,a可以调用do

void registerSomeEventListener(SomeEventListener l);

此外,在许多情况下,B需要支持多个侦听器,因此我重新实现了注册和notifyAll逻辑。

我知道的一种通用方法是拥有一些EventListener(由A实现)和EventNotificationer(由B实现)类。在这种情况下,每个事件都由一个字符串标识,a实现方法:

void eventNotified(string eventType);

我认为这不是一个好的解决方案。如果A侦听多个事件,它将导致许多if-else语句,并且当仅在侦听器或通知程序中更改事件名称时,可能会导致错误。

我想知道在C++中实现观察者模式的正确方法是什么?

看看boost::signals2。它提供了一种通用机制来定义其他对象可以注册的"信号"。然后,信号所有者可以通过"发射"信号来通知观察者。受试者将信号定义为成员,然后跟踪连接的观察者,并在启动时通知他们,而不是注册方法。信号是静态类型的,并接受具有匹配签名的每个函数。这样做的优点是不需要继承,因此与传统的观测器继承层次结构相比,耦合性较弱。

class Subject {
public:
    void setData(int x) {
         data_ = x;
         dataChanged(x);
     }
     boost::signals2<void (int)> dataChanged;
private:
    int data_;
};
class Observer {
public:
    Observer(Subject& s) {
        c_ = s.dataChanged.connect([&](int x) {this->processData(x);});
    }
    ~Observer() {
        c_.disconnect();
    }
private:
    void processData(int x) {
        std::cout << "Updated: " << x << std::endl;
    }
    boost::signals2::connection c_;
};
int main() {
    Subject s;
    Observer o1(s);
    Observer o2(s);
    s.setData(42);
    return 0;
}

在本例中,主题保存一些int数据,并在数据更改时通知所有注册的观察者。

假设您有一个通用的事件触发对象:

class base_invoke {
public:
    virtual ~base_invoke () {};
    virtual void Invoke() = 0;
}

但你想在不同类型的对象上触发事件,所以你从基础上派生:

template<class C>
class methodWrapper : public base_invoke {
public:
typedef void (C::*pfMethodWrapperArgs0)();
    C *             mInstance;
    pfMethodWrapperArgs0 mMethod;
public:
    methodWrapper(C * instance, pfMethodWrapperArgs0 meth) 
    : mInstance(instance) 
{
        mMethod = meth;
    }
    virtual void Invoke () {
        (mInstance->*mMethod)();
    }
}

现在,如果您为指向base_invoke的指针集合创建一个包装器,那么您可以调用每个激发对象,并在您想要的任何类上发出任何方法的信号。

您也可以将此集合类变成烧制对象的工厂。简化工作。

class Event {
protected:
    Collection<base_invoke *> mObservers;
public:
    // class method observers
    template<class C>
    void Add (C * classInstance, typename methodWrapper<C>::pfMethodWrapperArgs0 meth) {
        methodWrapper<C> * mw = NEW(methodWrapper<C>)(classInstance, meth);
        mObservers.Add(ObserverEntry(key, mw));
    }
    void Invoke () {
        int count = mObservers.Count();
        for (int i = 0; i < count; ++i) {
            mObservers[i]->Invoke();
        }
    }
};

你已经完成了艰苦的工作。在您希望侦听器订阅的任何位置添加一个Event对象。您可能想要扩展它以允许删除侦听器,并且可能需要一些函数参数,但核心基本相同。