情景介绍
假如现在有一份数据,是一个班级每天的出勤人数,在程序中我们需要用不同的方式去显示这份数据(比如折线图,柱状图等)。同时,当数据被改变的时候,所有以该数据为基础的图也要进行更新,在这种情境中,观察者模式是一个很好的选择。
换个例子,想想报纸和杂志的订阅过程:
- 报社的业务就是出版报纸;
- 当向某家报社订阅报纸,只要报社有新报纸出版,就会给你送来。只要你是他们的订户,就会一直受到新报纸;
- 当不再想要这家报社的报纸的时候,取消订阅,他们就不会再送新报纸;
- 只要报社还在运营,就会一直有人向他们订阅或取消订阅报纸;
出版者(主题/报社)+订阅者(观察者/人)= 观察者模式
代码详情
由上述可知,观察者模式应有两部分:被观察者和观察者!有种听君一席话,如听一席话的感觉(bushi)
闲话少说,上代码!
被观察者
首先,是被观察者
的接口:
/**
* 被观察者 主题
*/
class Subject {
public:
//注册(订阅)
virtual void registerObserver(const std::shared_ptr<Observer> &observer) = 0;
//移除(取关)
virtual void removeObserver(const std::shared_ptr<Observer> &observer) = 0;
//通知观察者它们观察的东西改变啦
virtual void notifyObservers() = 0;
virtual ~Subject() = default;
};
其次,是一个实际的被观察者
:
class WeatherData : public Subject {
public:
WeatherData(float temp, float humidity, float pressure) : temp(temp), humidity(humidity), pressure(pressure) {};
//注册
void registerObserver(const std::shared_ptr<Observer> &observer) override {
observerVector.push_back(observer);
}
//移除
void removeObserver(const std::shared_ptr<Observer> &observer) override {
//这么写会不会有什么问题,不过测试的结果还是符合预期的
auto target = std::find(observerVector.begin(), observerVector.end(), observer);
if (target != observerVector.end()) {
observerVector.erase(target);
}
}
void notifyObservers() override {
for (const auto &tmp: observerVector) {
//通知观察者应该更新啦
tmp->update(temp, humidity, pressure);
}
}
void measurementsChanged() {
notifyObservers();
}
void setMeasurements(float temp, float humidity, float pressure) {
this->temp = temp;
this->humidity = humidity;
this->pressure = pressure;
measurementsChanged();
}
float getTemp() const {
return temp;
}
float getHumidity() const {
return humidity;
}
float getPressure() const {
return pressure;
}
private:
float temp{0.0};
float humidity{0.0};
float pressure{0.0};
std::vector<std::shared_ptr<Observer>> observerVector;
};
观察者
介绍完被观察者
,现在可以介绍观察者
了。首先是接口:
/**
* 观察者
*/
class Observer {
public:
//作为观察者,update方法是必要的,被观察者需要调用此方法
virtual void update(float temp, float humidity, float pressure) = 0;
virtual ~Observer() = default;
};
在本例中,还用到了另一个接口,用于提供显示
方法:
class DisplayElement {
public:
virtual void display() = 0;
~DisplayElement() = default;
};
接下来实现:
class CurrentConditionsDisplay : public Observer, public DisplayElement {
public:
CurrentConditionsDisplay() = default;
void display() override {
std::cout << "Current conditions: " << temperature
<< "F degrees and " << humidity
<< "% humidity" << std::endl;
}
void update(float temp, float humidity, float pressure) override {
this->temperature = temp;
this->humidity = humidity;
display();
}
private:
float temperature;
float humidity;
};
class StatisticsDisplay : public Observer, public DisplayElement {
public:
StatisticsDisplay() = default;
void display() override {
std::cout << "Avg/Max/Min temperature = " << (tempSum / numReadings)
<< "/" << maxTemp << "/" << minTemp << std::endl;
}
void update(float temp, float humidity, float pressure) override {
tempSum += temp;
numReadings++;
if (temp > maxTemp) {
maxTemp = temp;
}
if (temp < minTemp) {
minTemp = temp;
}
display();
}
private:
float maxTemp{0.0f};
float minTemp{200};
float tempSum{0.0f};
int numReadings;
};
也可以进行自定义实现哦。
示例
void observerTest() {
Observer::WeatherData weatherData(0, 0, 0);
std::shared_ptr<Observer::Observer> currentDisplay = std::make_shared<Observer::CurrentConditionsDisplay>();
std::shared_ptr<Observer::Observer> forecast = std::make_shared<Observer::ForecastDisplay>();
std::shared_ptr<Observer::Observer> heatDisplay = std::make_shared<Observer::HeatIndexDisplay>();
std::shared_ptr<Observer::Observer> statistics = std::make_shared<Observer::StatisticsDisplay>();
weatherData.registerObserver(currentDisplay);
weatherData.registerObserver(forecast);
weatherData.registerObserver(heatDisplay);
weatherData.registerObserver(statistics);
weatherData.setMeasurements(80, 65, 40.3f);
weatherData.removeObserver(forecast);
weatherData.setMeasurements(78, 90, 29.2f);
}
总结
观察者模式架构图如下:
观察者模式
观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新;
设计原则
- 为交互对象之间的松耦合设计而努力;