不同 Observable 的观察者模式

Observer Pattern For Different Observables

我想知道处理可能包含不同数据的 Observables 的合适方法是什么。使用天气数据类比:

假设我有不同的气象站记录数据。让我们说湿度,温度和压力。一个站可能只有记录温度的能力,而其他站则所有三个等等。

我想做的是:

这里有一些事情: 参数比 3 多很多。大约 30 个,并且可以增长。我想在我的基础可观察对象中实现 getTemperature()getPressure()getHumidity() 等,并在相关的 类 中覆盖它们。否则它 returns 什么都没有。我还创建了一个 Flags 匿名结构,它是为 Observable 和 Observer 指定的,只有当它们匹配时才会记录数据。

我想知道以下内容: 这是最好的设计吗?匹配 Flags 的责任应该在 Observer 身上吗?有没有更优雅的解决方案?

我不一定要查找代码讲义。只是关于一个好的实施的想法。

谢谢!

根据我的经验,观察者模式最好使用面向对象设计。

您可以创建一个 Observer < Weather > 和一个 Observable < Weather >,其中包含一个被观察的对象,一个 Weather 对象。伪代码示例:

public class Weather()
{
    ... Temperature
    ... Pressure
    ... Humidity
}

如果给定的实现有多个可观察数据,只需让它也为该类型实现 Observable。

因此,您可以拥有一个 ColorableWeatherState 对象,它既是 Observable < Weather > 又是 Observable < Color > 并且可以被关心颜色的观察者和关心天气的观察者订阅。它们可以是分开的。它们可能是同一个对象。具体实现由您决定。

因为您已经 Flags 确定了可以观察到的事物的种类,即

enum Measurement {
    Temperature = 0x00001
,   Humidity = 0x00002
,   Pressure = 0x00004
};

您可以重用它来通过数据识别测量值,而不是通过方法名称识别它们。换句话说,而不是让界面看起来像这样

struct observable {
    Measurement provides() {
        return Temperature | Humidity | Pressure;
    }
    double getTemperature() ...
    double getHumidity() ...
    double getPressure() ...
};

这样做:

struct observable {
    Measurement provides() {
        return Temperature | Humidity | Pressure;
    }
    double measure(Measurement measurementId) {
        ...
    }
};

这会给你一个统一的界面,观察者和可观察者完全通过数据匹配。

但是,measure 的所有实现都需要 "dispatch" 基于看起来像 switch 的数字,这并不理想。有一个解决方案:你可以在基础 class 中放置一个单一的非虚拟实现,然后使用常规虚拟调度:

struct observable_base {
    double measure(Measurement measurementId) {
        switch(measurementId) {
        case Temperature: return getTemperature();
        case Humidity: return getHumidity();
        case Pressure: return getPressure();
        }
        return 0;
    }
protected:
    virtual double getTemperature() { return 0; }
    virtual double getHumidity() { return 0; }
    virtual double getPressure() { return 0; }
};

首先,以下是我的看法。有很多方法可以解决您的问题,也许其他方法更好。我告诉你我是如何解决这类问题的。

我不会用所有可能的方法定义一个基本的可观察对象,这是不好的风格。一个 base class 应该只定义方法,它实现了。此外,它很难扩展,你必须同时编写观察者和可观察对象的代码,并且必须同时编译它们。如果将来使用额外的通信层,比如网络,就更难抽象了。

如果您的值具有相同的类型,请使用一个 getter 方法并为其提供参数以 select 结果:

 double getValue(enumMyValueType type);

如果您有不同的类型,例如字符串和双打,我会使用一种不同于 getValueDoublesgetValueString 的变体(如 boost::variants)。在您的情况下,应避免仅按类型区分的不同 getter。保持你的 observable class 小而稳定。如果您使用自己的 return 类型,则无需重新编码整个路径即可轻松使用颜色或氧气等新值对其进行扩展。 为可观察对象定义一个自己的 return 类型 class 比定义一个大的基础 class 更好,因为您可以将有关值的多个信息放在一起,例如:

temperature
source
timestamp

pressure
source
timestamp

扩展类型不会影响您的观察者和可观察对象,它们仍然是轻量级的。

终于。只有观察者可以决定他想要什么以及他是否匹配。观察者应该询问可观察对象并决定注册什么和在哪里注册。可观察对象不需要知道观察者想要什么。到目前为止,我看到的任何协商系统都失败了(在 DirectShow 的情况下,它失败得很严重)。

这可能是一个糟糕的答案,但我想知道为什么我们不能将可观察对象 return 设置为变量(或指向它们的指针)的结构,并将无效字段设置为 NaN(或 Null 用于指针)或其他一些标识符。

我能看到的一个问题是,无论观察者请求什么,它都会强制 Observable 提供一切。

那么:

在对 observable 的 get() 调用中,它 return 是一个指向数据 getter 的函数指针结构。如果 observable 可以提供该数据,那么 getter 就不是 Null。然后观察者可以选择一个getter,检查它是否为空,然后最终得到它想要的数据。