在我的应用程序的许多情况下,我需要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 ++中实现观察者模式的正确方法是什么?
答案 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对象。您可能希望扩展它以允许删除侦听器,并且可能采用一些函数参数但核心几乎相同。