什么是观察者模式
·
观察者模式(Observer Pattern)是一种行为型设计模式,其核心思想是建立对象之间的一对多依赖关系,当一个对象(被观察者)的状态发生变化时,所有依赖它的对象(观察者)会自动收到通知并更新。
这种模式类似现实中的 “订阅 - 发布” 机制(如公众号订阅:公众号更新后,所有订阅者会收到推送)。
观察者模式的核心角色
-
被观察者(Subject):
维护一个观察者列表,提供添加、移除观察者的方法,以及通知所有观察者的方法。当自身状态变化时,主动通知所有观察者。 -
观察者(Observer):
定义一个更新接口,当收到被观察者的通知时,执行相应的更新操作。 -
具体被观察者(ConcreteSubject):
被观察者的具体实现,包含实际的状态数据,状态变化时触发通知。 -
具体观察者(ConcreteObserver):
观察者的具体实现,实现更新接口,根据被观察者的状态变化执行具体逻辑。
适用场景
- 一个对象的变化需要联动影响多个其他对象(如 GUI 界面中,数据变化时多个组件需要同步更新)。
- 需要解耦被观察者和观察者(观察者无需知道被观察者的细节,只需响应通知)。
- 事件监听系统(如按钮点击、消息通知等)。
C++ 代码示例
以 “气象站” 为例:气象站(被观察者)收集天气数据,多个显示面板(观察者)实时显示数据。
#include <iostream>
#include <vector>
#include <memory>
#include <string>
// 前向声明
class Subject;
// 1. 观察者接口
class Observer {
public:
virtual ~Observer() = default;
// 纯虚函数:接收被观察者的通知并更新
virtual void update(float temperature, float humidity, float pressure) = 0;
};
// 2. 被观察者接口
class Subject {
public:
virtual ~Subject() = default;
// 添加观察者
virtual void registerObserver(Observer* observer) = 0;
// 移除观察者
virtual void removeObserver(Observer* observer) = 0;
// 通知所有观察者
virtual void notifyObservers() = 0;
};
// 3. 具体被观察者:气象站
class WeatherData : public Subject {
private:
std::vector<Observer*> observers; // 观察者列表
float temperature; // 温度
float humidity; // 湿度
float pressure; // 气压
public:
void registerObserver(Observer* observer) override {
observers.push_back(observer);
}
void removeObserver(Observer* observer) override {
// 从列表中移除观察者
auto it = std::find(observers.begin(), observers.end(), observer);
if (it != observers.end()) {
observers.erase(it);
}
}
void notifyObservers() override {
// 通知所有观察者更新数据
for (Observer* observer : observers) {
observer->update(temperature, humidity, pressure);
}
}
// 当气象数据更新时调用,触发通知
void measurementsChanged() {
notifyObservers();
}
// 设置气象数据(模拟传感器更新)
void setMeasurements(float temp, float humi, float press) {
temperature = temp;
humidity = humi;
pressure = press;
measurementsChanged(); // 数据变化,通知观察者
}
};
// 4. 具体观察者1:当前天气面板
class CurrentConditionsDisplay : public Observer {
private:
float temperature;
float humidity;
// 可选:保存对被观察者的引用,便于后续移除
Subject* weatherData;
public:
// 构造时注册为观察者
CurrentConditionsDisplay(Subject* subject) : weatherData(subject) {
weatherData->registerObserver(this);
}
// 实现更新接口
void update(float temp, float humi, float press) override {
temperature = temp;
humidity = humi;
display(); // 收到数据后立即显示
}
// 显示当前天气
void display() {
std::cout << "当前天气:温度 " << temperature
<< "℃,湿度 " << humidity << "%" << std::endl;
}
};
// 5. 具体观察者2:气压面板
class PressureDisplay : public Observer {
private:
float pressure;
public:
void update(float temp, float humi, float press) override {
pressure = press;
display();
}
void display() {
std::cout << "当前气压:" << pressure << " hPa" << std::endl;
}
};
// 客户端代码
int main() {
// 创建被观察者:气象站
WeatherData* weatherData = new WeatherData();
// 创建观察者并自动注册
CurrentConditionsDisplay* currentDisplay = new CurrentConditionsDisplay(weatherData);
PressureDisplay* pressureDisplay = new PressureDisplay();
weatherData->registerObserver(pressureDisplay); // 手动注册
// 模拟气象数据更新
std::cout << "--- 第一次数据更新 ---" << std::endl;
weatherData->setMeasurements(25.5f, 60.0f, 1013.2f);
std::cout << "\n--- 第二次数据更新 ---" << std::endl;
weatherData->setMeasurements(26.8f, 55.0f, 1012.5f);
// 移除一个观察者
weatherData->removeObserver(pressureDisplay);
std::cout << "\n--- 移除气压面板后的数据更新 ---" << std::endl;
weatherData->setMeasurements(24.3f, 65.0f, 1014.0f);
// 清理资源
delete currentDisplay;
delete pressureDisplay;
delete weatherData;
return 0;
}
代码说明
-
观察者接口(
Observer):定义update()方法,所有观察者必须实现该方法以响应通知。 -
被观察者接口(
Subject):定义registerObserver()、removeObserver()、notifyObservers()三个核心方法,规范被观察者的行为。 -
具体被观察者(
WeatherData):- 维护气象数据(温度、湿度、气压)和观察者列表。
- 当数据更新(
setMeasurements())时,调用notifyObservers()通知所有观察者。
-
具体观察者(
CurrentConditionsDisplay、PressureDisplay):- 实现
update()方法,接收新数据并更新自身状态。 - 通过
display()方法展示数据(具体业务逻辑)。
- 实现
观察者模式的优缺点
优点:
- 实现被观察者与观察者的解耦,双方无需知道对方细节,只需依赖接口。
- 支持动态添加 / 移除观察者,灵活性高。
- 符合 “开闭原则”,新增观察者无需修改被观察者代码。
缺点:
- 如果观察者数量过多,通知过程可能耗时较长。
- 观察者之间可能存在依赖关系,通知顺序可能导致逻辑问题(需谨慎设计)。
观察者模式广泛应用于事件驱动系统(如 GUI 框架、消息队列、订阅系统等),是实现 “松耦合” 设计的重要手段。
更多推荐
所有评论(0)