观察者模式是行为型设计模式,也叫发布 - 订阅模式(Publish-Subscribe)
核心一句话总结:建立「一对多」的依赖关系,一个对象(被观察者 / 主题)的状态发生变化时,所有依赖它的对象(观察者)都会收到自动通知并更新。
一、它到底解决什么问题?
不使用观察者模式的痛点
假设你做一个天气监测系统:
- 天气温度变了 → 要手动通知手机、电视、平板、车载设备
- 代码里要写一堆
phone.update()、tv.update()、pad.update() - 新增一个设备(手表),就要改原有代码 →严重耦合、违反开闭原则
- 状态同步混乱,容易漏通知、错通知
使用观察者模式的效果
- 天气(被观察者)只管自己更新状态
- 所有设备(观察者)主动订阅天气
- 天气一变 →自动广播通知所有订阅者
- 新增设备只需要加一个观察者,不用改原有代码
二、观察者模式标准结构(4 大核心角色)
观察者模式固定分为4 个角色,缺一不可:
| 角色 | 英文名 | 作用 |
|---|---|---|
| 抽象主题(抽象被观察者) | Subject | 定义注册、移除、通知观察者的接口,维护观察者列表 |
| 具体主题(具体被观察者) | ConcreteSubject | 实现抽象主题,存储自身状态,状态变化时调用notify()通知所有观察者 |
| 抽象观察者 | Observer | 定义接收通知、更新数据的接口(update()) |
| 具体观察者 | ConcreteObserver | 实现抽象观察者,收到通知后执行业务逻辑(更新、展示、处理) |
三、核心工作流程(极简步骤)
- 观察者向被观察者注册(订阅)
- 被观察者保存所有注册的观察者到列表
- 被观察者状态发生变化
- 被观察者调用
notify(),遍历列表通知所有观察者 - 观察者执行
update(),完成自身更新
五、C++ 完整代码实现(逐行解析)
以气象站通知多个设备为案例,代码可直接运行、注释详细
#include <iostream> #include <vector> #include <string> using namespace std; // ===================== 前置声明:观察者类 ===================== class Observer; // ============================================== // 1. 抽象主题(抽象被观察者):核心接口定义 // ============================================== class Subject { protected: // 观察者列表:存储所有订阅的观察者 vector<Observer*> observerList; public: // 注册观察者(订阅) virtual void attach(Observer* observer) = 0; // 移除观察者(取消订阅) virtual void detach(Observer* observer) = 0; // 通知所有观察者(核心方法) virtual void notify() = 0; virtual ~Subject() = default; }; // ============================================== // 2. 抽象观察者:接收通知的接口 // ============================================== class Observer { public: // 收到通知后执行更新:参数是被观察者传递的新状态 virtual void update(const string& weatherInfo) = 0; virtual ~Observer() = default; }; // ============================================== // 3. 具体主题:气象站(真正的被观察者) // ============================================== class WeatherStation : public Subject { private: // 被观察者的核心状态:天气信息 string weather; public: // 实现注册观察者 void attach(Observer* observer) override { observerList.push_back(observer); cout << "一个设备已订阅气象站" << endl; } // 实现移除观察者 void detach(Observer* observer) override { for (auto it = observerList.begin(); it != observerList.end(); it++) { if (*it == observer) { observerList.erase(it); cout << "一个设备已取消订阅" << endl; break; } } } // 核心:通知所有观察者 void notify() override { cout << "\n===== 气象站天气更新,开始通知所有设备 =====" << endl; for (Observer* obs : observerList) { // 调用观察者的update方法,传递最新天气 obs->update(weather); } } // ============= 业务方法:设置天气(状态改变)============= void setWeather(const string& newWeather) { this->weather = newWeather; cout << "\n气象站更新天气:" << newWeather << endl; // 状态改变,自动通知 notify(); } }; // ============================================== // 4. 具体观察者:手机设备 // ============================================== class PhoneObserver : public Observer { public: void update(const string& weatherInfo) override { cout << "[手机设备] 收到最新天气:" << weatherInfo << endl; } }; // ============================================== // 5. 具体观察者:电视设备 // ============================================== class TVObserver : public Observer { public: void update(const string& weatherInfo) override { cout << "[电视设备] 收到最新天气:" << weatherInfo << endl; } }; // ============================================== // 6. 具体观察者:智能手表设备 // ============================================== class WatchObserver : public Observer { public: void update(const string& weatherInfo) override { cout << "[智能手表] 收到最新天气:" << weatherInfo << endl; } }; // ============================================== // 客户端测试代码 // ============================================== int main() { // 1. 创建被观察者:气象站 WeatherStation* station = new WeatherStation(); // 2. 创建观察者:多个设备 Observer* phone = new PhoneObserver(); Observer* tv = new TVObserver(); Observer* watch = new WatchObserver(); // 3. 设备订阅气象站(注册观察者) station->attach(phone); station->attach(tv); station->attach(watch); // 4. 气象站改变天气(状态变化 → 自动通知) station->setWeather("晴天,25℃,微风"); // 5. 手表取消订阅 station->detach(watch); // 6. 气象站再次更新天气 station->setWeather("小雨,18℃,降温"); // 释放内存 delete watch; delete tv; delete phone; delete station; return 0; }运行结果
plaintext
一个设备已订阅气象站 一个设备已订阅气象站 一个设备已订阅气象站 气象站更新天气:晴天,25℃,微风 ===== 气象站天气更新,开始通知所有设备 ===== [手机设备] 收到最新天气:晴天,25℃,微风 [电视设备] 收到最新天气:晴天,25℃,微风 [智能手表] 收到最新天气:晴天,25℃,微风 一个设备已取消订阅 气象站更新天气:小雨,18℃,降温 ===== 气象站天气更新,开始通知所有设备 ===== [手机设备] 收到最新天气:小雨,18℃,降温 [电视设备] 收到最新天气:小雨,18℃,降温六、观察者模式优缺点
优点
- 彻底解耦被观察者只依赖抽象观察者,观察者只依赖抽象主题,双方互不关心具体实现
- 符合开闭原则新增观察者(设备)无需修改被观察者代码,直接实现 Observer 接口即可
- 广播式通知一次状态改变,自动通知所有订阅者,不用手动挨个调用
- 动态订阅 / 取消运行时可随时注册、移除观察者,灵活性极高
缺点
- 观察者过多时,通知效率低同步通知会阻塞,需异步优化
- 可能出现循环依赖观察者通知被观察者,被观察者又通知观察者 → 死循环
- 通知顺序不可控默认按注册顺序通知,无法指定优先级
- 调试难度增加状态变化链式传递,问题追踪复杂
八、观察者模式 vs 发布 - 订阅模式(易混点)
日常开发中常混用,但严格区分:
- 观察者模式:只有被观察者 ↔ 观察者,直接通信,耦合度稍高
- 发布 - 订阅模式:多一个中间代理(Broker / 事件总线),发布者和订阅者完全零耦合
比如:
- RabbitMQ 是发布 - 订阅(有 Broker)
- 原生观察者模式是直接通知(无中间层)
九、终极总结
- 核心:一对多依赖,状态自动广播通知
- 角色:抽象主题 + 具体主题 + 抽象观察者 + 具体观察者
- 流程:订阅 → 状态变 → 通知 → 更新
- 核心价值:解耦、开闭原则、动态扩展
- 一句话记忆:你关注我,我变了就告诉你,不用我挨个找你。