不同观察者的观察者模式

时间:2015-02-27 21:16:11

标签: c++ design-patterns observer-pattern

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

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

我要做的是以下内容:

  • 观察者指定他们想要记录的数据。说,只有温度。
  • observable指定他们可用的数据。
  • 如果观察者和观察者都匹配,则数据由observable处理。

以下是一些事项: 有比3更多的参数。像30这样的东西,这可以增长。我想在我的基础可观察中实现getTemperature()getPressure()getHumidity()等等,并在相关类中重写它们。否则它什么也不返回。我还创建了为Observable和Observer指定的Flags匿名结构,并且只有当它们匹配数据时才会被记录。

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

我不一定在寻找代码讲义。关于良好实施的想法。

谢谢!

4 个答案:

答案 0 :(得分:2)

因为你已经有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的所有实现都需要根据看起来像switch的数字“调度”,这是不理想的。有一个解决方案:您可以在基类中放置一个非虚拟实现,然后使用常规虚拟调度:

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

答案 1 :(得分:1)

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

您可以创建一个Observer<天气>和一个Observable<天气>使用一个被观察的对象,一个Weather对象。伪代码样本:

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

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

所以,你可以有一个ColorableWeatherState对象,它同时是Observable<天气>和Observable<颜色>可以由关心色彩的观察者和关心天气的观察者订阅。他们可能是分开的。它们可能是同一个对象。由您决定实施。

答案 2 :(得分:1)

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

我不会用所有可能的方法定义一个基本的observable,那就是糟糕的风格。基类应该只定义方法,它才能实现。另外,它很难扩展,你必须同时编写观察者和可观察对象,并且必须对它们进行编译。如果将来使用其他通信层,如网络,则更难以抽象。

如果您的值具有相同的类型,请使用一个getter方法并为其选择一个参数来选择结果:

 double getValue(enumMyValueType type);

如果你有不同的类型,例如字符串和双打,我会使用一种变体(如boost :: variants),而不是getValueDoublesgetValueString。在您的情况下,应避免使用仅按类型区分的不同吸气剂。保持您的可观察班级小而稳定。如果使用自己的返回类型,则无需重新编码整个路径,就可以使用颜色或氧气等新值扩展它。 定义一个自己的返回类型类比为observables定义一个大的基类更好,因为你可以将有关该值的几个信息放在一起,如:

temperature
source
timestamp

pressure
source
timestamp

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

最后。只有观察者应该决定他想要什么以及他是否匹配。观察员应该询问观察者并决定注册的内容和位置。观察者不应该知道观察者想要什么。直到现在我看到的任何谈判系统都失败了(在DirectShow的情况下,它失败了很多)。

答案 3 :(得分:0)

这可能是一个错误的答案,但我想知道为什么我们不能让observable返回一个变量结构(或指向它们的指针),无效字段设置为NaN(或Null指针)或一些其他标识符。

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

然后怎么样:

get()对observable的调用中,它返回一个指向数据getter的函数指针结构。如果observable可以提供该数据,则getter不是Null。然后观察者可以选择一个getter,检查它是否为null,然后finall获取它想要的数据。