观察者模式
本课属于 Coddy C++ 学习旅程的“面向对象编程”单元。第 94 课,共 104 课。
观察者模式定义了对象之间的一对多依赖关系:当一个对象(主题)改变状态时,它的所有依赖对象(观察者)都会自动收到通知。这非常适合事件系统、UI 更新,或任何需要多个对象响应变化的场景。
这种模式涉及两个关键角色:维护观察者列表并通知它们的主题,以及实现更新接口的观察者:
#include <iostream>
#include <vector>
#include <algorithm>
class Observer {
public:
virtual void update(int value) = 0;
virtual ~Observer() = default;
};
class Subject {
std::vector<Observer*> observers;
int state = 0;
public:
void attach(Observer* obs) {
observers.push_back(obs);
}
void detach(Observer* obs) {
observers.erase(
std::remove(observers.begin(), observers.end(), obs),
observers.end());
}
void setState(int value) {
state = value;
notify();
}
void notify() {
for (Observer* obs : observers) {
obs->update(state);
}
}
};
class Display : public Observer {
std::string name;
public:
Display(const std::string& n) : name(n) {}
void update(int value) override {
std::cout << name << " received: " << value << "\n";
}
};
int main() {
Subject sensor;
Display screen1("Screen1"), screen2("Screen2");
sensor.attach(&screen1);
sensor.attach(&screen2);
sensor.setState(42); // 两个显示器都收到了通知
}调用 setState() 时,主体会遍历所有已注册的观察者,并调用它们的 update() 方法。观察者可以随时注册或注销,这使系统更加灵活,耦合度也更低。
当一个对象发生变化时需要更新其他对象,并且你不希望这些对象紧密耦合在一起,可以使用 Observer。
挑战
简单让我们使用观察者模式构建一个 Weather Station 监控系统。你将创建一个跟踪温度数据的气象站,并在温度变化时自动通知多个显示设备:这是该模式在现实世界中的经典应用。
你将把代码分到三个文件中:
Observer.h:定义观察者接口和一个具体的显示观察者。创建一个抽象
Observer类,其中包含一个纯虚方法update(double temperature),观察者通过实现该方法来接收通知;此外还要包含一个虚析构函数。然后创建一个继承自
Observer的TemperatureDisplay类。每个显示设备都有一个通过构造函数设置的名称(字符串)。调用update()时,它应打印:[DisplayName]: Temperature is [temperature] degreesWeatherStation.h:创建一个维护温度并通知观察者的主题。你的
WeatherStation类应当:- 存储观察者指针列表和当前温度(初始化为
0.0) - 包含一个用于注册观察者的
attach(Observer* obs)方法 - 包含一个用于移除观察者的
detach(Observer* obs)方法 - 包含一个
setTemperature(double temp)方法,用于更新温度并通知所有已附加的观察者 - 包含一个私有
notify()方法,用当前温度调用每个观察者的update()
- 存储观察者指针列表和当前温度(初始化为
main.cpp:演示观察者模式的实际应用。读取三个输入:
- 第一个显示设备的名称(字符串)
- 第二个显示设备的名称(字符串)
- 一个温度值(double)
创建一个
WeatherStation和两个TemperatureDisplay对象,并使用提供的名称。将两个显示设备都附加到气象站,然后将温度设为输入值。两个显示设备都应自动收到更新并打印消息。之后,移除第一个显示设备,并将温度设为比输入值高 5 度的新温度。只有第二个显示设备应收到这次更新。
例如,输入 Kitchen、Bedroom 和 22.5 时:
Kitchen: Temperature is 22.5 degrees
Bedroom: Temperature is 22.5 degrees
Bedroom: Temperature is 27.5 degrees输入 Office、Lobby 和 18.0 时:
Office: Temperature is 18 degrees
Lobby: Temperature is 18 degrees
Lobby: Temperature is 23 degrees请注意,观察者模式创建了一个低耦合系统:气象站不需要了解具体的显示设备,只需通知所有正在监听的对象。移除某个观察者后,它就会自动停止接收更新。
亲手试试
#include <iostream>
#include <string>
#include "WeatherStation.h"
using namespace std;
int main() {
// 读取输入
string display1Name, display2Name;
double temperature;
cin >> display1Name;
cin >> display2Name;
cin >> temperature;
// TODO: 创建一个 WeatherStation 对象
// TODO: 使用输入的名称创建两个 TemperatureDisplay 对象
// TODO: 将两个显示器都连接到气象站
// TODO: 将温度设置为输入值
// (两个显示器都应输出各自的消息)
// TODO: 断开第一个显示器的连接
// TODO: 将温度设置为比输入值高 5 度
// (只有第二个显示器应输出消息)
return 0;
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: C++ 在线编译器