解释
观察者模式用于建立对象间的一对多依赖,当主题(Subject)状态变化时,所有观察者(Observers)自动收到通知。
Observer 模式应该可以说是应用最多、影响最广的模式之一,因为 Observer 的一个实例 Model/View/Control(MVC)结构在系统开发架构设计中有着很重要的地位和意义。
典型应用场景
- GUI事件响应(如按钮点击触发多个控件更新)
- 数据实时同步(如股票价格变动通知多个客户端)
- 游戏引擎中的事件系统(如角色死亡触发任务失败/成就解锁)
不使用观察者模式的缺点
假设要实现一个天气监测系统,当温度变化时通知手机App和LED屏幕。若直接硬编码调用所有依赖对象,会导致以下问题:
- 高耦合主题类(WeatherStation)直接依赖具体观察者类,违反依赖倒置原则。
- 扩展困难新增观察者类型(如Web界面)需修改主题类代码,违反开闭原则。
- 代码冗余需要手动管理所有观察者的调用逻辑。
代码对比
1. 不使用观察者模式(硬编码调用)
#include <iostream>
// 具体显示设备类(紧耦合)
class MobileApp {
public:
void update(float temp) {
std::cout << "Mobile App: Temperature updated to " << temp << "°C\n";
}
};
class LEDScreen {
public:
void refresh(float temp) {
std::cout << "LED Screen: New temperature = " << temp << "°C\n";
}
};
// 主题类(直接管理所有设备)
class WeatherStation {
MobileApp* mobileApp; // 直接依赖具体类
LEDScreen* ledScreen; // 新增设备需添加成员变量
float temperature;
public:
WeatherStation(MobileApp* app, LEDScreen* screen)
: mobileApp(app), ledScreen(screen) {}
void setTemperature(float temp) {
temperature = temp;
// 必须显式调用所有设备的方法
mobileApp->update(temp);
ledScreen->refresh(temp);
}
};
int main() {
MobileApp app;
LEDScreen screen;
WeatherStation station(&app, &screen);
station.setTemperature(25.5);
}
缺点分析:
- 添加新设备(如WebInterface)需修改WeatherStation的构造函数和setTemperature方法。
- 主题类与具体观察者类紧密耦合,单元测试困难。
- 无法动态增减观察者(如运行时移除LED屏幕)。
2. 使用观察者模式优化
#include <iostream>
#include <vector>
#include <algorithm>
// 抽象观察者接口(解耦核心)
class Observer {
public:
virtual void update(float temp) = 0;
virtual ~Observer() = default;
};
// 具体观察者实现
class MobileApp : public Observer {
public:
void update(float temp) override {
std::cout << "Mobile App: Temperature updated to " << temp << "°C\n";
}
};
class LEDScreen : public Observer {
public:
void update(float temp) override {
std::cout << "LED Screen: New temperature = " << temp << "°C\n";
}
};
// 主题类(面向接口编程)
class WeatherStation {
std::vector<Observer*> observers; // 统一管理抽象接口
float temperature;
public:
void addObserver(Observer* obs) { observers.push_back(obs); }
void removeObserver(Observer* obs) {
observers.erase(std::remove(observers.begin(), observers.end(), obs), observers.end());
}
void setTemperature(float temp) {
temperature = temp;
for (auto obs : observers) {
obs->update(temp); // 统一调用接口
}
}
};
int main() {
WeatherStation station;
MobileApp app;
LEDScreen screen;
station.addObserver(&app);
station.addObserver(&screen);
station.setTemperature(25.5); // 输出两者的通知
// 动态移除观察者
station.removeObserver(&screen);
station.setTemperature(26.0); // 仅输出Mobile App
}
优点:
- 主题类仅依赖抽象接口Observer,与具体实现解耦。
- 支持动态增删观察者,扩展性极强(新增设备只需实现Observer接口)。
- 符合开闭原则,无需修改已有代码即可扩展功能。
总结
场景 | 不使用观察者模式 | 使用观察者模式 |
---|---|---|
耦合度 | 高(直接依赖具体类) | 低(依赖抽象接口) |
扩展性 | 差(需修改主题类代码) | 优(新增观察者无需修改主题类) |
动态管理 | 不支持运行时增删 | 观察者支持 |
代码维护成本 | 高(重复调用逻辑) | 低(统一管理逻辑) |
观察者模式通过抽象接口和动态注册机制,有效解决了硬编码调用带来的耦合性问题,是解耦对象间通知关系的经典方案。