如何设计具有不同类型的通知和观察器的观察者模式?
How do I design an Observer pattern with different types of notifications and observers?
场景:假设我有一个基类SketchbookEventObserver
和派生类MouseClickObserver
、TouchObserver
和TextChangedObserver
。
所有这些SketchbookEventObserver
都必须发出网络请求,其中包含有关所发生事件的数据:
MouseClickObserver
- 鼠标单击的坐标。
TouchObserver
- 触摸的坐标和持续时间。
TextChangedObserver
- 带有文本框标识符的新旧文本。
所有这些观察者都在UIEventRegistry
类中注册。当事件发生时,它会在每个观察者上调用OnEvent
,并作为参数传递:
- 事件类型 - 鼠标单击/触摸/文本已更改。这由 ID 表示。
- 有关事件的数据 - 如上所述。每种类型的事件都有不同类型的数据。
但是,我不能在每个派生类中使用不同的输入参数重写OnEvent
。如果我有输入参数泛型和多态,比如EventData
带有GetData()
函数,我仍然需要在派生类中覆盖GetData()
EventData
,这将具有不同的返回值。这也是不可能的。
另一种选择是这些观察者之间没有任何继承,并将它们视为单独的实体。EventRegistry
将有一个每种类型的观察者的数组/列表,其中它们的类型是已知的,然后为鼠标单击事件调用mMouseClickObservers[i].OnEvent()
,为触摸事件调用mTouchObservers[i].OnEvent()
,依此类推。
但这意味着EventRegistry
需要了解具体类的知识,如果EventRegistry
是不同库/包的一部分,则需要公开这些类。
有没有更好的方法来设计这个?
一种方法是从EventData类派生MouseClickEventData,TouchEventData,TextChanged类。并强制转换事件数据,以便每个数据都有特定的类。
OnEvent(EventData *data) {
if (data->type == MOUSE_CLICK) {
MouseClickEventData *mData = dynamic_cast<MouseClickEventData*>(data);
// use mData->getCoordinates();
}
if (data->type == TEXT_CHANGED) {
TextChangedEventData *tData = dynamic_cast<TextChangedEventData*>(data);
// use tData->getNewText();
}
....
}
为了根据观察者的类型和被观察的事物来改变观察者的行为,我们需要使用双重调度。访客模式使用双重调度来允许一组访客观察一组事件。我不会在这里提供实现,但伪代码类似于:
interface SketchbookEventObserver:
void handle(MouseClickEvent event);
void handle(TouchEvent event);
interface SketchbookEvent:
void accept(SketchbookEventObserver observer);
class MouseClickObserver implements SketchbookEventObserver:
void handle(MouseClickEvent event):
...
void handle(TouchEvent event):
...
interface MouseClickEvent implements SketchbookEvent:
void accept(SketchbookEventObserver observer):
observer.handle(this);
另一种方法是为每种类型的事件注册一个侦听器。这是Java及其UI框架所采用的方法。例如:
class Component:
void registerMouseClickListener(MouseClickListener listener):
...
void registerTouchListener(TouchListener listener):
...
interface MouseClickListener:
void handle(MouseClickEvent event);
interface TouchListener:
void handle(TouchEvent event);
另一种选择是这些观察者之间没有任何继承,并将它们视为单独的实体。
这是我最喜欢的选项,也是我多次提倡的选项,因为它可以保持事件的类型安全性。如果每个事件都不同,请不要通过同一 API 处理它们。
但这意味着
EventRegistry
需要了解具体的类......
MouseClickObserver
、TouchObserver
和TextChangedObserver
都应该是抽象的。现在每个抽象可能只有一个实现;但设计不应该强制执行这一点。
- 函数如何通知用户它基于函数原型抛出异常?
- 如何在ECS框架中更新组件数据和通知系统
- 当 I2C 值在C++中发生变化时收到通知
- 如何设计具有不同类型的通知和观察器的观察者模式?
- 当可输入框在窗口中处于活动状态时获得通知的任何方法
- 是否有必要获取锁并在不需要唤醒线程时通知condition_variable?
- 如何在点击通知时捕获用户的点击操作
- 在通知提升间处理条件变量时未按住锁会导致问题
- 通知条件变量后使用互斥锁
- 如何在不设置值的情况下通知 c++ 未来?
- 使用 ISdigit 通知任何检测到的非法字符
- 如何在可观察量列表中使用 RxCpp 运算符?
- Qt:如何通知对象已建立涉及它的信号槽连接
- 提升进程间named_condition_any不通知
- Windows Toast 通知不适用于 MS CPP 示例代码
- C++ Poco - 如何创建通知队列的向量?
- 当通知迭代器参数初始化为空列表的开头时,list::insert 行为是什么?
- C++ Windows 通知:使用依赖模板名称
- std::condition_variable - 等待多个线程通知观察者
- C++ 双重调度观察器通知