假定我们需要为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()方法,可以在更新观察者时具有更多的弹性,比如什么时候可以
// 不更新,什么时候更新