事件侦听器的最佳设计模式

Best design pattern for event listeners

本文关键字:设计模式 最佳 侦听器 事件      更新时间:2023-10-16

使用c++ &MFC我已经创建了一个类,使它更容易添加拖放功能的CWnd对象。这真的没什么特别的。当前的用法如下:

  1. 创建cdropllistener对象
  2. 在CDropListener对象上调用一个方法,说明您希望它对哪个文件扩展名做出反应,并提供一个函数指针,说明当文件被删除时要调用什么
  3. 注册CWnd对象
  4. 当窗口被销毁时删除CDropListener对象
  5. 如果您想要不同的CWnd
  6. 文件扩展名,请重复上述所有步骤

为每个侦听器创建类成员变量有点麻烦,我只是想知道哪种设计模式更适合于此。我只需要成员对象,这样我就可以在最后对它们进行delete。我想我可以用一个数组来存储它们,它会简化一点,但我也认为可能有一个更好的方法,你可以调用一个类似于DropListener::RegisterListener(CWnd* wnd, CString& extension, void(*callback) callback)的静态函数,它为你处理所有的创建/注册/删除

我不熟悉MFC,但从面向对象的角度来看,您的设计可以改进。

首先确定需求的哪些方面最有可能更改,然后确定隔离这些更改所需的接口是什么:

变化:

  • 您想要监听的内容(事件)
  • 你想要采取的动作(回调)

接口:

  • 向事件通知器中添加与事件相关的回调的机制
  • 从通知器调用回调的机制

所以你需要一个Event接口,一个Callback接口和一个Notifier接口。

在c++中有一个方便的东西叫做std::function<T>,其中T是任何可调用类型(指向函数的指针,functor, lambda)。所以你应该用它来封装你的回调,给你的用户更多的自由。

你想支持多少种事件类型?这将告诉您是否需要支持不同的Event对象,以及您的注册看起来如何:

// For example if you support just `Drop` events:
void addDropListener(std::function<T> callback);
// If you support many events:
void addListener(Event::Type evType, std::function<T> callback);

一旦你回答了这个问题,你需要决定一个"回调"看起来像什么(T在上面的例子)。这可以返回一个值(如果您想要成功验证)或抛出一个特定的异常类型(确保记录契约)。然后询问是否需要已触发事件的副本(通常需要)。假设您很高兴只通过异常收到错误通知,那么您可以像这样定义std::函数:

typedef std::function<void (const Event&)> EventCallback;
我建议你的Notifier实现者使用std::vector<EventCallback>std::map<Event::Type, std:vector<EventCallback>。如果您希望只支持一种事件类型或为所有事件调用所有侦听器,那么第一个方法很有用。当您只想通知侦听器特定类型的事件时,第二个选项很方便。

在任何情况下,如果你发现你需要改变你的类代码来适应行为的微小变化,那么你需要一些重构。

希望有帮助。:)