观察者模式(Observer Pattern)
定义
观察者模式是一种行为型设计模式,定义了一种 一对多的依赖关系,使得当一个对象的状态发生改变时,所有依赖它的对象都会收到通知并自动更新。
别名
- 监听者模式(Listener Pattern)
- 发布-订阅模式(Publish-Subscribe Pattern)
核心思想
- 一对多依赖:一个主题对象(Subject)被多个观察者对象(Observer)订阅。
- 事件通知:当主题对象发生状态改变时,会通知所有订阅它的观察者对象。
- 解耦:主题对象和观察者对象之间松耦合,主题只负责通知,具体如何处理由观察者实现。
适用场景
- 系统中存在一对多的依赖关系:
- 多个模块需要依赖同一数据源。
- 需要动态订阅:
- 不确定具体哪些模块会对事件感兴趣。
- 事件驱动开发:
- 如 GUI 程序、消息通知机制。
类结构
-
主题(Subject):
- 定义一个接口,用于添加、删除观察者对象。
- 提供通知方法(如
notifyObservers()
)。 - 维护观察者列表。
-
观察者(Observer):
- 定义一个接口,实现更新方法(如
update()
)。 - 用于处理主题的通知。
- 定义一个接口,实现更新方法(如
-
具体主题(ConcreteSubject):
- 实现主题接口,维护主题的具体状态。
- 当状态改变时通知观察者。
-
具体观察者(ConcreteObserver):
- 实现观察者接口,根据通知做出响应。
代码实现
问题描述
- 一个数据源(主题)被多个图表(观察者)订阅,当数据源发生变化时,需要通知所有图表更新。
完整代码
#include <iostream>
#include <vector>
#include <unordered_map>
#include <list>// 抽象观察者
class Observer {
public:virtual void handleMessage(int messageID) = 0;virtual ~Observer() = default;
};// 具体观察者1:观察者感兴趣的是消息 1 和 2
class Observer1 : public Observer {
public:void handleMessage(int messageID) override {if (messageID == 1 || messageID == 2) {std::cout << "Observer1 received message: " << messageID << std::endl;} else {std::cout << "Observer1 ignored message: " << messageID << std::endl;}}
};// 具体观察者2:观察者感兴趣的是消息 2
class Observer2 : public Observer {
public:void handleMessage(int messageID) override {if (messageID == 2) {std::cout << "Observer2 received message: " << messageID << std::endl;} else {std::cout << "Observer2 ignored message: " << messageID << std::endl;}}
};// 具体观察者3:观察者感兴趣的是消息 1 和 3
class Observer3 : public Observer {
public:void handleMessage(int messageID) override {if (messageID == 1 || messageID == 3) {std::cout << "Observer3 received message: " << messageID << std::endl;} else {std::cout << "Observer3 ignored message: " << messageID << std::endl;}}
};// 主题类
class Subject {
private:// 维护消息ID到观察者列表的映射std::unordered_map<int, std::list<Observer*>> observers;public:// 添加观察者void addObserver(int messageID, Observer* observer) {observers[messageID].push_back(observer);}// 通知观察者void notifyObservers(int messageID) {if (observers.find(messageID) != observers.end()) {for (Observer* observer : observers[messageID]) {observer->handleMessage(messageID);}} else {std::cout << "No observers for message: " << messageID << std::endl;}}
};// 主函数
int main() {Subject subject;Observer1 observer1;Observer2 observer2;Observer3 observer3;// 添加观察者subject.addObserver(1, &observer1);subject.addObserver(2, &observer1);subject.addObserver(2, &observer2);subject.addObserver(1, &observer3);subject.addObserver(3, &observer3);// 测试通知subject.notifyObservers(1); // 消息1通知subject.notifyObservers(2); // 消息2通知subject.notifyObservers(3); // 消息3通知subject.notifyObservers(4); // 无人订阅的消息return 0;
}
运行结果
Observer1 received message: 1
Observer3 received message: 1
Observer1 received message: 2
Observer2 received message: 2
Observer3 received message: 3
No observers for message: 4
代码解析
-
观察者接口(Observer):
- 定义抽象方法
handleMessage
,由具体观察者实现。
- 定义抽象方法
-
具体观察者(Observer1, Observer2, Observer3):
- 每个观察者实现了
handleMessage
,并根据感兴趣的消息 ID 处理事件。
- 每个观察者实现了
-
主题类(Subject):
- 维护了一个
unordered_map
,将消息 ID 映射到感兴趣的观察者列表。 - 提供
addObserver
添加观察者。 - 提供
notifyObservers
方法,通知所有感兴趣的观察者。
- 维护了一个
-
运行逻辑:
- 消息 ID 为 1 时,通知
Observer1
和Observer3
。 - 消息 ID 为 2 时,通知
Observer1
和Observer2
。 - 消息 ID 为 3 时,通知
Observer3
。 - 消息 ID 为 4 时,无观察者收到通知。
- 消息 ID 为 1 时,通知
优缺点
优点:
-
松耦合:
- 主题对象和观察者对象之间松耦合。
- 主题不需要知道观察者的具体实现。
-
动态订阅:
- 可以动态添加、移除观察者。
-
扩展性好:
- 新增主题或观察者时,不需要修改已有代码。
缺点:
-
性能问题:
- 如果观察者较多,通知开销可能较大。
-
过度依赖事件:
- 事件链较长时,可能导致难以追踪问题。
适用场景
- GUI 事件处理:如按钮点击事件。
- 发布-订阅系统:如消息队列。
- 数据模型与视图同步:如 MVC 模式。
观察者模式与其他模式对比
模式 | 用途 | 关键特性 |
---|---|---|
观察者模式 | 监听状态变化,通知观察者 | 一对多依赖 |
中介者模式 | 通过中介者减少对象直接交互 | 中心化协调 |
发布-订阅模式 | 广播消息到多个订阅者 | 更松散的关系,通常异步 |
总结
- 观察者模式的核心:解决一对多依赖问题,实现松耦合的事件通知机制。
- 实践建议:适用于动态订阅的场景,注意避免过多观察者导致性能问题。
- 应用范围:GUI、消息队列、实时数据更新等。