观察者模式是指在对象之间定义一对多的依赖,这样一来,当一个对象改变状态时,依赖它的对象都会收到通知,并自动更新,这里有主题和观察者之分,一般也可以称为发布/订阅模式。最简单的例子就是订阅报纸,比如说自己作为观察者,订阅了"人民日报"这个主题,那么当人民日报出新的报纸的时候就会及时送到自己即观察者的手中。
观察者模式的实现方法有很多种,一般以实现Subject接口(C++是抽象类)和Observer接口(C++是抽象类)最为常见,并且多以“推”的方式更新消息,即可观察者(主题)在状态发生变化时,自动将新的状态信息推送给所有的观察者。当然,也有一些地方使用“拉”的方式,即观察者在需要状态更新的时候主动去获取新的状态信息,即从可观察者那里拉消息出来。由于“拉”的方式比较复杂,并且可控性不太好,所以经常使用“推”的方式,不过要注意这种方式也有一些弊端,比如说所有观察者都会被动地收到新的状态信息,有时候可能不需要这些信息,或者不需要很频繁地获取这些信息,就会导致观察者收到很多“垃圾消息”,影响系统的性能,不过这些缺陷可以通过一些手段去优化和弥补。
观察者模式一般需要四个基本的角色:
(1)抽象主题(Subject):它把所有观察者对象的引用保存到一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。
(2)具体主题(ConcreteSubject):将有关状态存入具体观察者对象;在具体主题内部状态改变时,给所有登记过的观察者发出通知。
(3)抽象观察者(Observer):为所有的具体观察者定义一个接口,在得到主题通知时更新自己。
(4)具体观察者(ConcreteObserver):实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态协调。
通过抽象的角色实现了统一的接口,根据项目需求可以开发出各种各样的具体观察者,而主题也不会随着具体观察者的变化而发生变化。
下面,通过一个例子来看看具体的实现。
下面依次是:ObserverPattern.h文件、ObserverPattern.cpp文件以及ObserverPatternTest.cpp文件。
// 观察者模式,使用推的方式 #ifndef OBSERVER #define OBSERVER #include <iostream> #include <iomanip> #include <list> #include <algorithm> using std::cout; using std::endl; using std::list; using std::find; using std::showpoint; using std::fixed; using std::setprecision; // 扮演接口作用的三个抽象类 class Observer; class Subject { public: Subject(){} virtual ~Subject(){} virtual void registerObserver(Observer* ob) = 0; virtual void removeObserver (Observer* ob) = 0; virtual void notifyObserver () = 0; }; class Observer { public: Observer(){} virtual ~Observer(){} virtual void update(float temperature, float humidity, float pressure) = 0; }; class DisplayElement { public: DisplayElement(){} virtual ~DisplayElement(){} virtual void display() = 0; }; // 可观察者即主题的具体实现类 class WeatherData : public Subject { public: void registerObserver(Observer* ob); void removeObserver (Observer* ob); void notifyObserver (); float getTemperature(); float getHumidity(); float getPressure(); void measurementsChanged(); void setMeasurements(float temperature, float humidity, float pressure); private: list<Observer*> listOb; float temperature; float humidity; float pressure; }; // 观察者的第一个具体实现类 class CurrentConditionsDisplay : public Observer, public DisplayElement { public: CurrentConditionsDisplay(WeatherData* wd) { this->weatherData = wd; weatherData->registerObserver(this); } void update(float temperature, float humidity, float pressure); void display(); private: float temperature; float humidity; Subject* weatherData; }; // 观察者的第二个具体实现类 class ForecastDisplay : public Observer, public DisplayElement { public: ForecastDisplay(WeatherData* wd) { currentPressure = 29.92f; this->weatherData = wd; weatherData->registerObserver(this); } void update(float temperature, float humidity, float pressure); void display(); private: float currentPressure; float lastPressure; Subject* weatherData; }; // 观察者的第三个具体实现类 class StatisticsDisplay : public Observer, public DisplayElement { public: StatisticsDisplay(WeatherData* wd) { maxTemp = 0.0f; minTemp = 200; tempSum = 0.0f; this->weatherData = wd; weatherData->registerObserver(this); } void update(float temperature, float humidity, float pressure); void display(); private: float maxTemp; float minTemp; float tempSum; int numReadings; Subject* weatherData; }; // 观察者的第四个具体实现类 class HeatIndexDisplay : public Observer, public DisplayElement { public: HeatIndexDisplay(WeatherData* wd) { heatIndex = 0.0f; this->weatherData = wd; weatherData->registerObserver(this); } void update(float temperature, float humidity, float pressure); void display(); private: float computeHeatIndex(float t, float rh); float heatIndex; Subject* weatherData; }; #endif
// 可观察者即主题的具体实现类 void WeatherData::registerObserver(Observer* ob) { listOb.push_back(ob); } void WeatherData::removeObserver(Observer* ob) { list<Observer*>::iterator it = find(listOb.begin(), listOb.end(), ob); if (it != listOb.end()) { listOb.erase(it); } } void WeatherData::notifyObserver() { list<Observer*>::iterator it = listOb.begin(); while (it != listOb.end()) { (*it)->update(temperature, humidity, pressure); ++it; } } float WeatherData::getTemperature() { return temperature; } float WeatherData::getHumidity() { return humidity; } float WeatherData::getPressure() { return pressure; } void WeatherData::measurementsChanged() { notifyObserver(); } void WeatherData::setMeasurements(float temperature, float humidity, float pressure) { this->temperature = temperature; this->humidity = humidity; this->pressure = pressure; measurementsChanged(); } // 观察者的第一个具体实现类 void CurrentConditionsDisplay::update(float temperature, float humidity, float pressure) { this->temperature = temperature; this->humidity = humidity; display(); } void CurrentConditionsDisplay::display() { cout << fixed << setprecision(1) << "Current conditions: " << temperature << "F degrees and " << humidity << "% humidity" << endl; } // 观察者的第二个具体实现类 void ForecastDisplay::update(float temperature, float humidity, float pressure) { lastPressure = currentPressure; currentPressure = pressure; display(); } void ForecastDisplay::display() { cout << "Forecast: "; if (currentPressure > lastPressure) { cout << "Improving weather on the way!" << endl; } else if (currentPressure == lastPressure) { cout << "More of the same" << endl; } else if (currentPressure < lastPressure) { cout << "Watch out for cooler, rainy weather" << endl; } } // 观察者的第三个具体实现类 void StatisticsDisplay::update(float temperature, float humidity, float pressure) { tempSum += temperature; numReadings++; if (temperature > maxTemp) { maxTemp = temperature; } if (temperature < minTemp) { minTemp = temperature; } display(); } void StatisticsDisplay::display() { cout << fixed << setprecision(1) << "Avg/Max/Min temperature = " << (tempSum / numReadings) << "/" << maxTemp << "/" << minTemp << endl; } // 观察者的第四个具体实现类 void HeatIndexDisplay::update(float temperature, float humidity, float pressure) { heatIndex = computeHeatIndex(temperature, humidity); display(); } float HeatIndexDisplay::computeHeatIndex(float t, float rh) { float index = (float)((16.923 + (0.185212 * t) + (5.37941 * rh) - (0.100254 * t * rh) + (0.00941695 * (t * t)) + (0.00728898 * (rh * rh)) + (0.000345372 * (t * t * rh)) - (0.000814971 * (t * rh * rh)) + (0.0000102102 * (t * t * rh * rh)) - (0.000038646 * (t * t * t)) + (0.0000291583 * (rh * rh * rh)) + (0.00000142721 * (t * t * t * rh)) + (0.000000197483 * (t * rh * rh * rh)) - (0.0000000218429 * (t * t * t * rh * rh)) + 0.000000000843296 * (t * t * rh * rh * rh)) - (0.0000000000481975 * (t * t * t * rh * rh * rh))); return index; } void HeatIndexDisplay::display() { cout << fixed << setprecision(5) << "Heat index is " << heatIndex << endl; }
#include "ObserverPattern.h" void main() { WeatherData weatherData; CurrentConditionsDisplay currentConditionsDisplay(&weatherData); ForecastDisplay forecastDisplay(&weatherData); StatisticsDisplay statisticsDisplay(&weatherData); HeatIndexDisplay heatIndexDisplay(&weatherData); weatherData.setMeasurements(80.0f, 65.0f, 30.4f); weatherData.setMeasurements(82.0f, 70.0f, 29.2f); weatherData.setMeasurements(78.0f, 90.0f, 29.2f); }
该例的运行结果如图1所示。
图1 运行结果
接下来给出这个例子的UML类图,如图2所示。
图2 UML类图
上面的例子是一个简单的天气预报系统,WeatherData继承了主题抽象类,其他的观察者继承了观察者抽象类,当主题中的天气信息发生变化的时候,会自动调用measurementsChanged()函数,从而将新的状态发送给所有的观察者,在主题中有一个list用来存储注册的观察者列表,也提供了一些函数用来注册或者注销观察者。而观察者则通过统一的update()和display()函数接口实现自己的天气预报更新及显示的功能。总体的关系大家看一下图2中的UML类图及代码就可以理解,其中的C++实现的语法关键点在于:
(1)抽象类作为基类,一般都要将析构函数写为虚函数;
(2)其中多处用到了指针,避免了抽象类不能实例化的情况,并且可以动态设计观察者的状态,不过要注意指针的处理,仔细一些就可以,其中一些地方没有加具体的错误判断和处理,大家在具体实现中最好加上这些处理,使得自己的系统更加完善。
总之,观察者模式的使用范围很广,也是非常有用的一种设计模式,大家可以在项目中借鉴本文的一些思想,不过其他的细节问题需要大家进一步深入理解,欢迎大家提出宝贵的意见。