如何设计具有不同类型的通知和观察者的观察者模式?

时间:2020-02-10 11:32:10

标签: c++ oop design-patterns

场景:假设我有一个基类SketchbookEventObserver和派生类MouseClickObserverTouchObserverTextChangedObserver

所有这些SketchbookEventObserver都必须向网络请求有关发生的事件的数据:

MouseClickObserver-鼠标单击的坐标。

TouchObserver-触摸的坐标和持续时间。

TextChangedObserver-具有文本框标识符的旧文本和新文本。

所有这些观察者都注册在UIEventRegistry类上。事件发生时,它将在每个观察者上调用OnEvent,并作为参数传递:

  1. 事件类型-鼠标单击/触摸/文本已更改。这由ID表示。
  2. 有关事件的数据-如上所述。 每种类型的事件将具有不同类型的数据

但是,我无法在每个派生类中使用不同的输入参数覆盖OnEvent。如果我有通用和多态的输入参数,例如使用EventData函数说GetData(),我仍然需要在GetData()的派生类中覆盖EventData返回值。这也是不可能的。

另一个选择是在这些观察者之间没有任何继承,并将它们视为独立的实体。 EventRegistry将具有每种类型的观察者的数组/列表,其中已知其类型,然后针对鼠标单击事件调用mMouseClickObservers[i].OnEvent(),对于触摸事件调用mTouchObservers[i].OnEvent(),依此类推。

但这意味着EventRegistry将需要具有有关具体类的知识,如果EventRegistry是不同库/包的一部分,则需要公开公开这些具体类。

有没有更好的方法来设计这个?

3 个答案:

答案 0 :(得分:1)

一种方法是可以从EventData类派生MouseClickEventData,TouchEventData,TextChanged类。并强制转换EventData,以便为每个数据提供特定的类。

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();
    }
    ....
}

答案 1 :(得分:1)

为了基于观察者的类型和被观察的事物来改变观察者的行为,我们需要使用double-dispatchVisitor Pattern使用双调度来允许一组访问者观察一组事件。我在这里不提供实现,但是伪代码类似于:

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);

答案 2 :(得分:1)

另一种选择是在这些观察者之间没有任何继承,并将它们视为单独的实体。

这是我最喜欢的选项,也是我多次提倡的选项,因为它可以维护事件的类型安全。如果每个事件都不相同,请不要通过相同的API来全部处理。

但这意味着EventRegistry将需要具有有关具体类的知识...

MouseClickObserverTouchObserverTextChangedObserver应该都是抽象的。现在,每个抽象可能只有一个实现。但设计不应强制执行该操作。