观察者模式(Observer Pattern)
定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会受到通知并且自动更新。并且有“推”和“拉”两种通知方式。
设计原则:为了交互对象之间的松耦合设计而努力
问题:
现在需要建立一个Internet 气象站,WeatherData 对象可以追踪目前的天气状况,现在有三个布告板,分别显示目前的状况,气象统计以及简单的预报。当WeatherData获取了最新的数据的时候,三种布告板必须实时更新。
下面来看一个例子:
鸭子对象过来告诉主题,它想当一个观察者
鸭子对象现在已经成为正式观察者
主题有了新数据,现在鸭子可以收到数据
老鼠对象要求从观察者中把自己除名
老鼠离开了
主题有了新数据,老鼠接收不到
定义观察者模式的类图:
#include<iostream>
#include<list>
using namespace std;
class Observer{
public:
virtual void update(float temp,float humidity, float pressure) = 0;
};
class Subject{
public:
virtual void registerObserver(Observer *o) = 0;
virtual void removeObserver(Observer *o) = 0;
virtual void notifyObservers() = 0;
};
class DisplayElement{
public:
virtual void display() = 0;
};
class WeatherData: public Subject{
public:
WeatherData(){
observers.clear();
}
void registerObserver(Observer *o){
observers.push_back(o);
}
void removeObserver(Observer *o){
observers.remove(o);
}
void notifyObservers(){
for( list<Observer *>::iterator it = observers.begin(); it != observers.end(); ++it){
(*it)->update(temperature,humidity,pressure);
}
}
void measurementsChanged(){
notifyObservers();
}
void setMeasurements(float temperature, float humidity,float pressure){
this->temperature = temperature;
this->humidity = humidity;
this->pressure = pressure;
//可以根据获取的数据来判断要不要设置
measurementsChanged();
}
private:
list<Observer*> observers;
float temperature;
float humidity;
float pressure;
};
class CurrentConditionsDisplay: public Observer,public DisplayElement{
public:
CurrentConditionsDisplay(Subject * weatherData){
this->weatherData = weatherData;
//注册成为观察者
this->weatherData->registerObserver(this);
}
~CurrentConditionsDisplay(){
}
void update(float temperature,float humidity,float pressure){
this->temperature = temperature;
this->humidity = humidity;
//继承的方法被重写
display();
}
void display(){
cout << "Current conditions:" << temperature
<< "F degrees and" << humidity
<< "%humidity" << endl;
}
private:
float temperature;
float humidity;
Subject * weatherData;
};
int main(){
WeatherData *weatherData = new WeatherData();
CurrentConditionsDisplay *currentDisplay = new CurrentConditionsDisplay(weatherData);
//可以自己定义其他的display 类型
//StatisticDisplay *statisticsDisplay = new StatisticDisplay(weatherData);
weatherData->setMeasurements(80,65,30.4f);
weatherData->setMeasurements(82,70,29.2f);
delete weatherData;
delete currentDisplay;
}
上述的设计充分体现了松耦合的原则。当两个对象之间松耦合,他们依然可以交互,但是不太清楚彼此的细节。观察者模式提供了一种对象设计,让主题和观察者之间松耦合。关于观察者的一切,主题只知道观察者实现了某个接口(Observer 纯虚类),并不需要知道观察者的具体类是谁、做了些什么或其他细节。任何时候我们可以增加新的观察者。因为主题唯一依赖的东西是一个实现Observer接口的对象列表,所以我们可以随时增加观察者。
假如我们有个新的具体类需要当观察者,我们不需要为了兼容新类型而修改主题的代码,所要做的就是在新的类里实现此观察者的接口,然后注册观察者即可。
上述的设计其实还存在一些问题,当数据改变的时候,主题“推”所有数据给观察者,即使这些数据不是必须的(上例中的pressure数据)。观察者模式还有另外一种思路,观察者提供getter 方法,当需要时,观察者来“拉”走自己所需要的数据。HeadFirst 上面是利用Java提供的接口实现的。