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

时间:2014-04-12 16:57:10

标签: c++ oop design-patterns observer-pattern

在我的应用程序的许多情况下,我需要A类将自己注册为B类的监听器,以便在发生事件时接收通知。在每种情况下,我都定义了一个单独的接口B实现,A可以调用do。例如,A将具有以下方法:

void registerSomeEventListener(SomeEventListener l);

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

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

void eventNotified(string eventType);

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

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

2 个答案:

答案 0 :(得分:0)

看看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数据,并在数据更改时通知所有已注册的观察者。

答案 1 :(得分:0)

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

class base_invoke {
public:
    virtual ~base_invoke () {};

    virtual void Invoke() = 0;
}

但是你想在不同类型的对象上触发事件,所以你从base:

派生
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对象。您可能希望扩展它以允许删除侦听器,并且可能采用一些函数参数但核心几乎相同。