这是Observer Pattern 的典型问题。
在 C++ 中,您可以使用模板轻松实现这一点。您可以使用我为 OSS 项目编写的一个。你可以从这个observer.h提取它
基本上你所做的是,你将鼠标处理程序对象声明为Dispatcher,而任何对象想要接收它作为Listener。在您的鼠标处理程序中,您调用 notify 方法,然后所有观察者(或侦听器)都将收到您的事件通知。这种方法的优点是,两个对象之间没有依赖关系,因此您可以轻松添加也可能对此事件感兴趣的不同对象,而无需更改对象本身。
这是一个简单的演示(使用 VS2015 编译,但也应该适用于 gcc,因为这是我最初开发它的地方)。
#include <iostream>
#include <vector>
template <typename... T> class Dispatcher;
template <typename... T> class Listener;
#define ListenerList std::vector
#define UNUSED(x) (x)
template <typename... T>
class Listener
{
public:
Listener(void)
{
}
virtual ~Listener(void)
{
}
virtual void handleNotification(Dispatcher<T...> *oSource, T... /* oEvent */)
{
// Default implementation does nothing
// which can be used as a null listener where
// a listener is expected but doesn't have
// a meaningful implementation.
UNUSED(oSource);
}
/**
* The invalidateDispatcher() call is sent when the dispatcher
* should no longer be accessed anymore. After this call, the listener
* will no longer receive any notifications and the dispatcher is
* destroyed, so the listener should not unregister with
* removeListener().
*/
virtual void invalidateDispatcher(Dispatcher<T...> const *oDispatcher)
{
UNUSED(oDispatcher);
}
};
template <typename... T>
class Dispatcher
{
public:
Dispatcher(void)
{
mAllowDuplicates = false;
}
virtual ~Dispatcher(void)
{
invalidate();
}
void allowDuplicates(bool bAllowDuplicates = true)
{
mAllowDuplicates = bAllowDuplicates;
}
/**
* After the invalidate() message is sent to the listeners,
* they will no longer receive any notifications and they should
* no longer access the dispatcher pointer as the object became invalid.
* When this call is sent, the listener also shouldn't
* unregister via removeListener().
*/
virtual void invalidate(void)
{
for (Listener<T...> * &listener : mListeners)
listener->invalidateDispatcher(this);
}
virtual void notify(T... oEvent)
{
for (Listener<T...> * &listener : mListeners)
listener->handleNotification(this, oEvent...);
}
/**
* Adds a listener to the dispatcher. A listener
* can attach itself multiple times, in which case
* it will receive as many notifications as it
* is registered. When the listener is removed
* it will remove all instances with a single call
* so there is no need to balance the addListener()
* with removeListener() calls.
*/
virtual void addListener(Listener<T...> *oListener)
{
if (!mAllowDuplicates)
{
if (listenerIndex(oListener) != -1)
return;
}
mListeners.push_back(oListener);
}
virtual void removeListener(Listener<T...> *oListener)
{
// The listener may have registered multiple times
// so we must remove all instances.
int i;
while ((i = listenerIndex(oListener)) != -1)
mListeners.erase(mListeners.begin() + i);
}
protected:
ListenerList<Listener<T...> *> &getListeners(void) const
{
return mListeners;
}
virtual int listenerIndex(Listener<T...> const *oListener) const
{
int i = -1;
for (Listener<T...> * const &listener : mListeners)
{
i++;
if (listener == oListener)
return i;
}
return -1;
}
private:
ListenerList<Listener<T...> *> mListeners;
bool mAllowDuplicates;
};
class Mousehandler : public Dispatcher<bool /* ButtonState */, int /* x Position */, int /* y Position */>
{
public:
Mousehandler(void) {}
void buttonePressed(int nButtonState, int x, int y)
{
if (nButtonState == 1) // Button up
notify(true, x, y);
else
notify(false, x, y); // Button down.
}
};
class MouseListener : public Listener<bool, int, int>
{
public:
MouseListener(int id) { mId = id; }
void handleNotification(Dispatcher<bool, int, int> *oSource, bool bButtonPress, int nX, int nY) override
{
UNUSED(oSource);
if (bButtonPress)
std::cout << mId << ": Button was pressed at " << nX << "/" << nY << std::endl;
else
std::cout << mId << ": Button was released at " << nX << "/" << nY << std::endl;
}
private:
int mId;
};
int main(int argc, char *argv[])
{
UNUSED(argc);
UNUSED(argv);
Mousehandler h;
MouseListener l1(1);
MouseListener l2(2);
h.addListener(&l1);
h.addListener(&l2);
h.buttonePressed(true, 10, 15);
h.buttonePressed(false, 20, 11);
return 0;
}
如果您使用的是较旧的编译器,您可能没有可变参数,在这种情况下,您必须更改模板以仅接受一个类型名,并且您必须使用指向结构或类的指针,如果您需要发送多个参数作为您的事件。使用 C++11 更简单,IMO 更清晰。
这是相同的,但在处理程序上使用多个事件。
class Mousehandler
: public Dispatcher<bool /* ButtonState */, int /* x Position */, int /* y Position */>
, public Dispatcher<int /* x Position */, int /* y Position */>
{
public:
typedef Dispatcher<bool, int , int > button_handler;
typedef Dispatcher<int, int > move_handler;
typedef Listener<bool, int, int > button_listener;
typedef Listener<int, int > move_listener;
public:
Mousehandler(void) {}
void buttonPressed(int nButtonState, int x, int y)
{
if (nButtonState == 1) // Button up
Dispatcher<bool, int, int>::notify(true, x, y);
else
Dispatcher<bool, int, int>::notify(false, x, y); // Button down.
}
void mouseMoved(int x, int y)
{
Dispatcher<int, int >::notify(x, y);
}
void addButtonListener(button_listener *pListener)
{
button_handler::addListener(pListener);
}
void addMoveListener(move_listener *pListener)
{
move_handler::addListener(pListener);
}
};
class MouseListener
: public Listener<bool, int, int>
, public Listener<int, int>
{
public:
MouseListener(int id) { mId = id; }
void handleNotification(Mousehandler::button_handler *oSource, bool bButtonPress, int nX, int nY) override
{
UNUSED(oSource);
if (bButtonPress)
std::cout << mId << ": Button was pressed at " << nX << "/" << nY << std::endl;
else
std::cout << mId << ": Button was released at " << nX << "/" << nY << std::endl;
}
void handleNotification(Mousehandler::move_handler *oSource, int nX, int nY) override
{
UNUSED(oSource);
std::cout << mId << ": Mouse moved to " << nX << "/" << nY << std::endl;
}
private:
int mId;
};
int main(int argc, char *argv[])
{
UNUSED(argc);
UNUSED(argv);
Mousehandler h;
MouseListener l1(1);
MouseListener l2(2);
h.addButtonListener(&l1);
h.addMoveListener(&l1);
// No need for movements on the second listener.
h.addButtonListener(&l2);
h.buttonPressed(true, 10, 15);
h.buttonPressed(false, 20, 11);
h.mouseMoved(12, 20);
h.mouseMoved(21, 23);
return 0;
}