观察者模式(Observer Pattern) 是一种行为型设计模式,它定义了对象之间 一对多的依赖关系,当一个对象(称为“主题”或“被观察者”)的状态发生改变时,所有依赖于它的对象(称为“观察者”)都会自动收到通知并更新。
🎯 核心思想
- 解耦:被观察者和观察者之间松耦合,彼此不知道对方的具体实现。
- 自动通知:被观察者状态变化时,自动广播通知给所有注册的观察者。
- 动态注册/注销:观察者可以动态地添加或移除。
🧱 角色组成
| 角色 | 说明 |
|---|
| Subject(主题 / 被观察者) | 它保存着被观察的状态。它维护一个观察者列表,并提供用于订阅、取消订阅和通知观察者的方法 |
| Observer(观察者) | 定义接收更新的接口,通知观察者主题的状态变化 |
| ConcreteSubject(具体主题) | 具体的被观察者,状态变化时通知所有观察者 |
| ConcreteObserver(具体观察者) | 实现更新逻辑,对接收到的通知做出反应 |
🌐 实际应用场景
| 场景 | 说明 |
|---|
| GUI 事件系统 | 按钮点击、鼠标移动等事件通知 |
| MVC 架构 | View 观察 Model 的变化 |
| 消息队列 / 发布-订阅系统 | 如 Kafka、Redis Pub/Sub |
| 游戏开发 | 角 |
| 日志监控 | 日志生成时通知色状态变化通知 UI 或 AI多个处理模块 |
| 股票价格监控 | 股价变动通知多个投资者对象 |
✅ 优点
| 优点 | 说明 |
|---|
| 松耦合 | 被观察者和观察者之间无直接依赖 |
| 可扩展性 | 可以动态添加/删除观察者 |
| 支持广播通信 | 一个主题可通知多个观察者 |
| 符合开闭原则 | 扩展新观察者无需修改主题 |
❌ 缺点
| 缺点 | 说明 |
|---|
| 性能问题 | 观察者过多时,通知可能耗时 |
| 内存泄漏风险 | 若未正确注销,观察者可能无法被释放(尤其在使用裸指针时) |
| 通知顺序不确定 | 通常不保证通知顺序 |
| 循环依赖 | 观察者反过来影响被观察者可能导致无限循环 |
🆚 与发布-订阅模式(Pub-Sub)的区别
| 特性 | 观察者模式 | 发布-订阅模式 |
|---|
| 耦合度 | 紧耦合(直接引用) | 松耦合(通过消息中间件) |
| 通信方式 | 同步调用 | 通常异步 |
| 中间件 | 无 | 有(如消息队列) |
| 适用范围 | 同一进程内 | 可跨进程、跨网络 |
🔍 简单说:观察者模式是“直接通知”,发布-订阅是“通过邮局中转”。
✅ 总结
观察者模式是一种强大且常用的设计模式,特别适合:
- 需要 状态变化通知 的场景
- 实现 事件驱动架构
- 构建 响应式系统
实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
| #include <iostream>
#include <vector>
#include <algorithm>
#include <memory>
#include <string>
using namespace std;
class Observer {
public:
virtual ~Observer() = default;
virtual void update(const string& newMessage) = 0;
};
class Subject {
public:
virtual ~Subject() = default;
virtual void sub_register(std::shared_ptr<Observer> observer) = 0; //由于register为关键字,所以函数
virtual void sub_unregister(const string& name, std::shared_ptr<Observer> observer) = 0; //为了注销提示名字,增加了name参数
virtual void notify() = 0;
};
class GroupChat : public Subject {
private:
vector<std::shared_ptr<Observer>> observers;
vector<string> messages;
public:
void sub_register(std::shared_ptr<Observer> observer) override {
observers.push_back(observer);
}
void sub_unregister(const string& name, std::shared_ptr<Observer> observer) override {
observers.erase(
std::remove(observers.begin(), observers.end(), observer),
observers.end()
);
std::cout << name << " Observer unregistered.\n";
}
void notify() override {
for (auto& observer : observers) {
observer->update(messages.back());
}
}
void sendMessage(const string& msg) {
messages.push_back(msg);
notify();
}
};
class Friend : public Observer {
private:
string name;
public:
Friend(const string& n) : name(n) {}
void update(const string& newMessage) override {
cout << name << " received a message: " << newMessage << endl;
}
string get_name() {
return name;
}
};
int main() {
auto chat = std::make_shared<GroupChat>();
auto friend1 = std::make_shared<Friend>("Alice");
auto friend2 = std::make_shared<Friend>("Bob");
auto friend3 = std::make_shared<Friend>("Charlie");
chat->sub_register(friend1);
chat->sub_register(friend2);
chat->sub_register(friend3);
chat->sendMessage("Hey everyone, let's plan our trip!");
chat->sub_unregister(friend1->get_name(), friend1);
chat->sendMessage("Alice get out!");
chat->sub_unregister(friend2->get_name(), friend2);
chat->sendMessage("Bob get out!");
chat->sub_unregister(friend3->get_name(), friend3);
chat->sendMessage("Charlie get out!");
return 0;
}
|
Refernece
https://www.geeksforgeeks.org/system-design/observer-pattern-c-design-patterns/
https://refactoring.guru/design-patterns/observer/cpp/example
https://www.bogotobogo.com/DesignPatterns/observer.php
https://medium.com/@lokeshbihani99/observer-pattern-in-c-366a1e9226f6
https://github.com/gayashanbc/observer-pattern-cpp