目录
什么是观察者模式?
观察者模式(Observer Pattern)是一种行为型设计模式,用于定义对象之间的一对多依赖关系。当一个对象(被观察者,Subject
)的状态发生变化时,所有依赖它的对象(观察者,Observer
)都会自动收到通知并更新。观察者模式使得对象之间的耦合关系松散化,增强了系统的可扩展性和灵活性。
观察者模式的特点:
- 一对多依赖:观察者模式允许一个对象(被观察者)通知多个依赖它的对象(观察者),使它们能够感知到状态的变化。
- 松散耦合:被观察者和观察者之间的关系是松耦合的,被观察者只需知道观察者实现了某个接口,而不需要关心观察者的具体实现。
- 事件通知机制:观察者模式通常用于实现事件通知机制,当某个事件发生时,被观察者可以通知所有的观察者,观察者根据通知做出反应。
观察者模式的结构
观察者模式的核心结构包括两个角色:
- 被观察者(Subject):维护一个观察者列表,提供注册和删除观察者的方法,并在状态变化时通知所有观察者。
- 观察者(Observer):定义一个接口,要求所有的观察者实现一个更新方法,当被观察者的状态变化时,观察者将被通知。
观察者模式的典型 UML 图:
+-----------------+ +-------------------+
| Subject |<-------| Observer |
+-----------------+ +-------------------+
| + attach() | | + update() |
| + detach() | +-------------------+
| + notify() |
+-----------------+
Subject
:被观察者,维护观察者列表并在状态改变时通知观察者。Observer
:观察者,定义update()
方法以在接收到通知时更新自身状态。
如何使用观察者模式设计事件通知机制
观察者模式非常适合事件驱动系统中的事件通知机制。下面通过 C++ 实现观察者模式,展示如何设计这种通知机制。
1. 定义观察者接口
观察者接口定义了一个 update()
方法,所有的观察者都必须实现这个方法以响应通知。
#include <iostream>
#include <vector>
#include <memory>
// 观察者接口
class Observer {
public:
virtual void update(const std::string& message) = 0;
virtual ~Observer() = default;
};
2. 定义被观察者(Subject)
被观察者负责维护一个观察者列表,提供注册(attach
)、注销(detach
)观察者的方法,并在状态发生变化时通知所有的观察者(notify
)。
class Subject {
private:
std::vector<std::shared_ptr<Observer>> observers;
public:
// 注册观察者
void attach(std::shared_ptr<Observer> observer) {
observers.push_back(observer);
}
// 注销观察者
void detach(std::shared_ptr<Observer> observer) {
observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end());
}
// 通知所有观察者
void notify(const std::string& message) {
for (auto& observer : observers) {
observer->update(message); // 调用观察者的更新方法
}
}
};
3. 实现具体的观察者类
观察者类需要实现 Observer
接口的 update()
方法,以便能够响应来自被观察者的通知。
class ConcreteObserver : public Observer {
private:
std::string name;
public:
ConcreteObserver(const std::string& name) : name(name) {}
// 当收到通知时更新状态
void update(const std::string& message) override {
std::cout << name << " received update: " << message << std::endl;
}
};
4. 将所有部分整合到一起
在 main()
函数中,我们可以创建被观察者和观察者对象,并通过 Subject
的 attach()
方法注册观察者。然后,通过调用 notify()
方法来通知观察者。
int main() {
// 创建被观察者
Subject subject;
// 创建观察者
std::shared_ptr<Observer> observer1 = std::make_shared<ConcreteObserver>("Observer 1");
std::shared_ptr<Observer> observer2 = std::make_shared<ConcreteObserver>("Observer 2");
// 注册观察者
subject.attach(observer1);
subject.attach(observer2);
// 被观察者发生变化,通知观察者
subject.notify("Event 1 occurred!");
// 注销一个观察者
subject.detach(observer1);
// 再次通知观察者
subject.notify("Event 2 occurred!");
return 0;
}
输出:
Observer 1 received update: Event 1 occurred!
Observer 2 received update: Event 1 occurred!
Observer 2 received update: Event 2 occurred!
5. 分析观察者模式
在上面的实现中:
Subject
维护了一个观察者列表,通过attach
和detach
方法管理观察者。Observer
定义了update()
方法,当Subject
发生变化时,会通知所有注册的观察者,调用它们的update()
方法。- 观察者对象可以随时注册或注销,它们之间是松耦合的。
Subject
只需知道Observer
实现了update()
方法,而不关心具体的观察者类。
观察者模式的优缺点
优点:
- 解耦:观察者和被观察者是松耦合的,被观察者不需要知道具体的观察者实现,增加了代码的灵活性和可维护性。
- 支持广播通信:一个被观察者可以同时通知多个观察者,自动更新它们的状态。
- 动态增加观察者:观察者可以在运行时动态添加或删除,而不影响被观察者的实现。
缺点:
- 通知开销:如果观察者数量很多,通知所有观察者可能导致性能问题。
- 顺序依赖:如果观察者之间存在依赖关系,通知的顺序可能导致问题。
- 观察者可能对状态变化反应不及时:由于观察者的状态更新依赖于通知机制,通知的时机和顺序可能会导致观察者的状态更新不及时。
观察者模式的使用场景
观察者模式非常适合以下场景:
- 事件驱动系统:当系统中某个对象的状态变化需要通知其他对象时,可以使用观察者模式。例如,GUI 事件处理(按钮点击、窗口大小变化等)、实时数据监控系统等。
- 订阅-发布机制:在一些消息传递系统中,发布者和订阅者可以通过观察者模式解耦。
- 模型-视图-控制器(MVC)架构:在 MVC 模式中,模型层的状态变化需要通知视图层进行更新,这通常使用观察者模式实现。
- 日志系统:在日志系统中,可以将日志的不同输出渠道(如文件、控制台、网络)作为观察者,当日志事件发生时,通知所有输出渠道处理日志。
改进与扩展
在实际应用中,观察者模式可以结合其他设计模式进行改进和扩展:
- 推模型和拉模型:
-
- 推模型:被观察者主动将变更的细节推送给观察者,观察者只能被动接受信息。
- 拉模型:被观察者通知观察者,但不提供具体的变化信息,观察者主动向被观察者拉取变化的详细内容。
目前实现的方式是推模型,你可以根据需求实现拉模型。
- 结合事件总线:对于复杂的系统,可以将事件总线与观察者模式结合,集中处理事件通知和订阅,避免多个对象直接相互依赖。
总结
- 观察者模式 是一种行为型设计模式,用于定义对象之间的一对多依赖关系。它通过将对象的状态变化通知多个依赖的观察者,实现对象之间的松耦合。
- 观察者模式特别适合事件驱动系统、消息传递系统、MVC 架构等场景,有助于提高系统的灵活性和可维护性。
- 通过使用
Subject
来管理观察者列表,观察者可以在运行时动态地添加和移除,观察者模式可以很好地解耦系统中的不同模块。