假定我们需要为Weather-O-Rama公司建立一个气象站系统,除已有的WeatherData有数据源类,还需要更新三个布告板的显示:目前状况(温度、湿度、气压)、气象统计和天气预报。
1 以下是一个可能的实现
class WeatherData
{
private:
//实例变量声明
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);
}
float getTemperature() {}
float getHumidity() {}
float getPressure() {}
};
上述实现有以下问题存在:
- 针对具体实现编程,而非针对接口(currentConditionsDisplay都是具体的实例)
- 针对每个新的布告板,都得修改代码(添加新的布告板到代码中)
- 无法在运行时动态增删布告板
- 尚未封装改变的部分(调用几个update的地方)
2 观察者模式
观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
主题是具有状态的对象,并且可以控制这些状态。另一方面,观察者使用这些状态,虽然这些状态不属于他们。
观察者模式提供了一种对象设计(设计原则:为了交互对象之间的松耦合设计而努力),让主题和观察者之间松耦合。
主题只知道观察者实现了某个接口,并不需要知道具体的观察者是谁,做了什么动作。同时,我们也可以在运行时增删观察者。
class Subject
{
public:
virtual void registerObserver(Observer o) = 0;
virtual void removeObserver(Observer o) = 0;
virtual void notifyObserver() = 0;
};
class Observer
{
public:
virtual void update(float temp, float humidity, float pressure) = 0;
};
class DisplayElement
{
public:
virtual void display() = 0;
};
// WeatherData实现接口
class WeatherData : public Subject
{
private:
std::vector<Observer*> observers;
float temprature;
float humidity;
float pressure;
public:
WeatherData()
{
observers = new vector<Observer*>();
}
void registerObserver(Observer* o)
{
observers.add(o);
}
void removeObserver(Observer* o)
{
observers.remove(o);
}
void notifyObserver()
{
for (Observer* o : observers)
{
o->update(temprature, humidity, pressure);
}
}
void measurementsChanged()
{
notifyObserver();
}
};
// 布告板实现Observer接口
// 为什么要保存对Subject的引用呢,构造完后好像并未使用?
// 以后可能想要取消注册,如果有对Subject的引用会比较方便。
class CurrentConditionsDisplay : public Observer, DisplayElement
{
private:
Subject* weatherData;
float temprature;
float humidity;
public:
CurrentConditionsDisplay(Subject* weatherData)
{
this->weatherData = weatherData;
weatherData->registerObserver(this);
}
void update(float temp, float humidity, float pressure)
{
this->temprature = temp;
this->humidity = humidity;
display();
}
void display()
{
std::cout << "Temprature:" << temprature << "Humidity:" << humidity << std::endl;
}
};
除使用上面的“推”方式主动将数据传送给观察者,还可以通过“拉”方式主动拉取数据(可以传递一个主题对象,或者数据对象给update方法,或者从私有变量中提取)。
// 主题本身作为变量,可以让观察者知道是哪个主题通知的
update(Observer o) {}
// Java的内置实现有setChanged()方法,可以在更新观察者时具有更多的弹性,比如什么时候可以
// 不更新,什么时候更新