设计模式02:观察者模式

151 阅读4分钟

需求

气象站需要做一个应用,有三种布告板,分别显示目前的状况/气象统计以及简单的预报。气象站已经有了一个WeatherData对象,用来追踪全天的天气状况(温度/湿度/气压)。当WeatherData对象更新时,三种布告板必须实时更新。

系统架构

此系统中的三个部分是气象站(获取实际气象数据的物理装置)、WeatherData对象(追踪来自气象站的数据,并更新布告板)以及布告板(显示目前天气状况给用户看)。

我们的工作就是利用WeatherData对象取得数据,并更新三个布告板:目前状况、气象统计和天气预报。

WeatherData

目前所知道的

  1. WeatherData类具有getter方法,可以获取三个测量值:温度、湿度和气压;
  2. 当新的数据就绪时,measurementsChanged()就会被调用;
  3. 需要实现3个使用天气数据的布告板。一旦WeatherData有了新数据,这些布告板需马上更新;
  4. 此系统必须可扩展,让其他开发人员创建定制的布告板

错误示范

class WeatherData {
public:
    void measurementsChanged() {
        float temp = getTemperature();
        float humidity = getHumidity();
        float pressure = getPressure();
        
        currentConditionsDisplay->update(temp, humidity, pressure);
        statisticsDisplay->update(temp, humidity, pressure);
        forecastDisplay->update(temp, humidity, pressure);
    }
}

上述代码包含以下几点问题:

  1. update()的参数列表完全相同,可考虑将其实现为一个统一的接口;
  2. 将来布告板可能会增多,需要改动WeatherData对象;
  3. 布告板对象与WeatherData严重耦合。

观察者模式

观察者模式定义了对象之间的一对多依赖,当一个对象状态改变时,他的所有依赖者都会收到通知并且自动更新。

一个栗子

我们看看报纸与杂志的订阅是怎么回事:

  1. 报社的业务是出版报纸;
  2. 用户向某家报社订阅报纸,只要报社有新报纸出版,就会给用户送过去。只要该用户一直是报社的订户,那么他会一直收到报纸;
  3. 当该用户不想要看报纸的时候,取消订阅,报社便不再订报纸;
  4. 只要报社还在,就会一直有人向其订阅报纸或者取消订阅报纸。

如果了解了报社的订阅是怎么回事,自然也就知道观察者模式是怎么回事,只是名字不太一样:出版者改称为“主题”(Subject),订阅者改称“观察者”(Observer)。

类图

松耦合的威力

当两个对象之间松耦合,它们依然可以交互,但是不太清楚彼此细节。观察者模式提供了一种对象设计,让主题和观察者之间松耦合。

在任何时候,我们都可以新增新的观察者而无需修改主题,主题唯一依赖的就是一个实现了Observer接口的对象列表。

同时,我们可以独立的复用主题或观察者,因为二者并不紧耦合。改变主题或者观察者的任意一方,并不会影响另一方。

设计原则

为了交互对象之间的松耦合设计而努力。

松耦合的设计之所以让我们建立有弹性的OO系统,能够应对变化,是因为对象之间的依赖降到了最低。

接口设计

class Subject{
public:
    virtual void regiterObserver(Observer* o) = 0;
    virtual void removeObserver(Observer* o) = 0;
    virtual void notifyObservers() = 0;
};

class Observer{
public:
    virtual void update(float temp, float humidity, float pressure) = 0;
};

class DisplayElement{
public:
    virtual void display() = 0;
}

Subject

class WeatherData : public Subject{
public:
    WeatherData(){
    	observers = {};
    }
    void registerObserver(Observer* o){
    	observers.add(o);
    }
    void removeObserver(Observer* o){
    	observers.erase(o);
    }
    
    void notifyObservers(){
    	for (auto o : observers){
            o->update();
        }
    }
    void measurementChanged(){
    	notifyObservers();
    }
private:
    std::set<Observer*> observers;
    float temperature;
    float humidity;
    float pressure;
};

Observer

class CurrentConditionDisplay : public Observer, public DisplayElement {
public:
    CurentConditionDisplay(Subject* weatherData) {
        this->weatherData = weatherData;
        this->weatherData->registerObserver(this);
    }
    void update(float temperature, float humidity, float pressure) {
        this->temperature = temperature;
        this->humidity = humidity;
        display();
    }
    
    void display(){
        std::cout << "Current conditions: " << temperature << " degrees and " 
        			<< humidity + < "% humidity";
    }
private:
    float temperature;
    float humidity;
    Subject* weatherData;
}

观察者模式的另一种实现

在上述实现中,observer是无法控制接收消息的频率的,只能被动地接收来自subject的信息。

参考书目

[1] Head First Design Patterns: Eric Freeman, Elisabeth Freeman, Bert Bates, Kathy Sierra