观察者模式(Observer):状态变了,订阅者自动收到通知
更新时间:2026-08-25。本文按"先看不用模式的困境 → 再用模式重构 → 类图对比分析"的结构,论述观察者模式。可运行代码:demos/design-patterns/observer。
一、问题场景:一个状态变化,多个对象要跟着响应
价格变动 → 行情面板/止损单/邮件提醒都要更新;用户登录 → 积分/审计/欢迎横幅都要反应;温度变化 → 空调/加湿器/显示屏都要调整。变化源(主题)与响应者(观察者)数量都是动态的。
二、不使用模式:主题直接持有并通知所有依赖方
cpp
// 行情类直接持有所有面板,每个都要 new 进来
class Market {
PricePanel* p1; StopLoss* s1; EmailAlert* e1;
void setPrice(double p) {
price_ = p;
p1->update(p); s1->update(p); e1->update(p); // 新增订阅者:改这里
}
};| 缺陷 | 说明 |
|---|---|
| 违反 OCP | 加订阅者 = 改主题类 |
| 违反 DIP | 主题依赖所有具体观察者 |
| 通知逻辑硬编码 | 顺序、数量都写死在主题里 |
| 无法动态增删 | 运行时不能订阅/退订 |
不使用模式的类图

三、使用模式:主题只依赖观察者接口,订阅者自注册
观察者模式定义主题(Subject)维护观察者列表(只依赖 Observer 抽象),提供 attach/detach/notify;观察者实现 update() 并自行注册。主题变化时广播通知,对具体观察者零依赖。
cpp
// observer.h
class Observer { // 观察者抽象
public:
virtual void update(double price) = 0;
virtual ~Observer() = default;
};
class Market { // 主题(Subject)
public:
void attach(Observer* o) { observers_.push_back(o); }
void detach(Observer* o) { /* 移除 */ }
void setPrice(double p) {
price_ = p;
for (auto* o : observers_) o->update(p); // 广播,不知道具体是谁
}
private:
std::vector<Observer*> observers_;
double price_;
};
class PricePanel : public Observer {
public:
void update(double p) override { std::cout << "panel: " << p; }
};
class StopLoss : public Observer { /* 触发止损逻辑 */ };
class EmailAlert : public Observer { /* 发邮件 */ };
// 运行时装配:订阅者自注册,主题无感知
Market m;
PricePanel panel; StopLoss sl;
m.attach(&panel); m.attach(&sl); // 可动态增减
m.setPrice(99.5);使用模式的类图

四、类图对比与分析
| 维度 | 不使用(硬编码通知) | 使用观察者 |
|---|---|---|
| 加订阅者 | 改主题类 | 新观察者类 + attach |
| 主题依赖 | 所有具体观察者 | 只有 Observer 抽象(DIP) |
| 动态增删 | 编译期写死 | 运行时 attach/detach |
| 通知顺序 | 硬编码 | 按注册顺序(可控制) |
| 一致性 | 必须手动逐个调 | notify 统一广播 |
分析结论:观察者把"一对多通知"从主题内部的硬编码变成观察者自注册的松散耦合。代价:时序不可控(通知顺序由注册顺序决定)、更新风暴(观察者连环更新)、内存泄漏(忘 detach 的悬挂指针,C++ 需注意生命周期)。本仓库性能视角:高频主题的遍历通知是热点,批量场景可考虑"快照 + 异步队列"。
五、适用边界
| 适用 | 不适用 |
|---|---|
| 一对多联动、订阅者动态增减 | 联动关系固定且少(直接调用即可) |
| 解耦触发者与响应者 | 更新链过长/循环依赖(需防环) |
| 事件/消息驱动的架构 | 极高频通知且实时性敏感(考虑事件总线/队列) |
C++ 注意:观察者持有裸指针,主题析构前必须 detach;更安全用 weak_ptr 或让主题管理观察者生命周期。与发布-订阅(pub/sub)的区别:观察者中主题直接持有观察者列表;pub/sub 中间隔一层消息代理,主题与订阅者完全互不知晓。
一句话总结
观察者模式让主题只依赖 Observer 抽象、订阅者自行注册,状态变化时统一广播通知——一对多联动从硬编码变为动态装配,加订阅者不再改主题;代价是通知时序与生命周期需自己管理,注意防悬挂指针与更新环。