C++ 观察者模式(Observer Pattern)
一、模式基础概述
1. 模式定义
观察者模式属于行为型设计模式 ,核心用于建立一对多 对象依赖关系。
当被观察者(目标主题)自身状态发生变更时,系统会自动通知所有已订阅的观察者对象,观察者接收到消息后主动执行自身更新业务逻辑,实现状态联动、事件自动推送,彻底解除事件发起方与事件处理方之间的强耦合。
2. 核心设计思想
- 事件解耦:被观察者仅负责状态变更与消息广播,不依赖任何具体观察者
- 订阅机制:支持程序运行时动态添加、移除观察者,灵活调整监听对象
- 统一通知:一次状态改变,批量推送消息至所有订阅者
- 被动响应:观察者无需轮询查询状态,等待主题主动推送通知即可
- 遵循设计原则 :符合开闭原则 、依赖倒置原则,新增监听业务无需修改原有主题代码
3. 四大核心标准角色
| 角色名称 | 英文名称 | 核心职责 | C++ 实现规范 |
|---|---|---|---|
| 抽象主题角色 | Subject | 定义观察者管理接口:订阅、取消订阅、批量通知 | 纯虚基类,提供统一订阅与通知虚接口 |
| 具体主题角色 | ConcreteSubject | 维护观察者容器列表,存储自身业务状态,状态变化触发通知 | 继承抽象主题,封装私有状态变量,实现状态修改与消息推送 |
| 抽象观察者角色 | Observer | 定义统一消息响应更新接口,规范所有观察者行为 | 纯虚基类,声明update()纯虚更新方法 |
| 具体观察者角色 | ConcreteObserver | 实现更新接口,接收主题推送消息,完成自身专属业务逻辑 | 继承抽象观察者,重写更新方法,处理差异化业务 |
4. 观察者模式与发布订阅模式核心区别
| 对比维度 | 传统观察者模式 | 发布-订阅模式 |
|---|---|---|
| 依赖关系 | 主题与观察者互相感知,存在直接依赖 | 引入事件总线中间层,发布者与订阅者完全无感知 |
| 通信方式 | 主题直接调用观察者更新方法 | 消息统一投递至总线,由总线分发推送 |
| 耦合程度 | 中度耦合 | 完全解耦 |
| 适用规模 | 小型本地事件监听、界面联动 | 跨模块、跨线程、跨进程大型事件分发 |
| 实现复杂度 | 代码简洁,上手简单 | 架构复杂,支持多事件分类订阅 |
二、适用场景与禁用场景
适用场景
- 业务存在一对多状态联动,一个数据变更需要同步触发多个业务行为
- GUI界面开发:控件点击、窗口状态切换、界面数据实时刷新
- 消息推送业务:系统公告、价格变动、配置热更新、告警通知
- 游戏开发:角色状态变化、技能触发、BUFF生效、场景事件监听
- 日志收集、数据统计、行为埋点等批量事件处理场景
- 硬件状态监听、设备上下线、传感器数据实时上报
不适用场景
- 事件数量极少、固定不变,无需动态增减监听对象
- 业务要求严格控制事件执行顺序,观察者无序通知无法满足
- 高频海量事件推送,大量观察者同步执行造成主线程阻塞
- 简单函数调用即可实现的轻量联动,过度使用造成代码冗余
三、模式优缺点深度分析
优点
- 业务高度解耦:发布逻辑与消费逻辑彻底分离,各自独立开发维护
- 拓展性极强:新增观察者无需修改主题源码,直接新增监听类即可
- 动态灵活管控:程序运行过程中自由订阅、取消监听,适配多变业务
- 消息自动联动:摒弃轮询机制,状态变更即时推送,响应效率更高
- 统一事件标准:所有观察者遵循同一更新接口,代码格式统一规范
缺点
- 通知顺序不可控:默认按照订阅顺序推送,无法自定义执行优先级
- 存在递归死循环风险:观察者更新逻辑内修改主题状态,易造成循环通知
- 性能损耗明显:订阅观察者数量过多时,批量通知会造成程序卡顿
- 调试排查难度大:事件传递链路较长,异常问题定位流程复杂
- 内存风险:未及时取消订阅容易造成对象常驻内存,引发内存泄漏
四、C++ 编码实现核心开发规范
- 抽象层统一使用纯虚类 定义接口,基类析构函数必须声明为
virtual虚析构,杜绝多态内存泄漏 - 具体主题内部使用
std::vector存储观察者指针/智能指针,统一管理订阅列表 - 状态修改逻辑与消息通知逻辑分离,仅在状态发生实际变更时触发推送
- 遍历通知观察者时,优先拷贝订阅列表,避免遍历过程中增删元素引发迭代器失效
- 大型项目优先使用
std::shared_ptr智能指针管理观察者生命周期,自动回收资源 - 多线程并发场景下,订阅、取消订阅、事件通知操作需添加互斥锁保证线程安全
- 观察者更新方法内禁止编写耗时阻塞逻辑,耗时业务建议异步抛入线程池执行
五、C++ 基础经典实现(新闻推送案例)
1. 抽象观察者 & 抽象主题
cpp
#include <iostream>
#include <vector>
#include <string>
#include <memory>
using namespace std;
// 抽象观察者
class Observer
{
public:
virtual ~Observer() = default;
// 统一更新接口,接收主题推送消息
virtual void update(const string& newsInfo) = 0;
};
// 抽象主题
class Subject
{
protected:
vector<Observer*> observerList;
public:
virtual ~Subject() = default;
// 订阅观察者
virtual void attach(Observer* observer)
{
observerList.push_back(observer);
}
// 取消订阅
virtual void detach(Observer* observer)
{
for (auto iter = observerList.begin(); iter != observerList.end(); ++iter)
{
if (*iter == observer)
{
observerList.erase(iter);
break;
}
}
}
// 批量通知所有观察者
virtual void notifyAll(const string& msg)
{
// 拷贝容器,防止遍历中修改列表崩溃
vector<Observer*> tempList = observerList;
for (auto obs : tempList)
{
obs->update(msg);
}
}
};
2. 具体被观察者(新闻发布平台)
cpp
// 具体主题:新闻发布平台
class NewsSubject : public Subject
{
private:
string currentNews;
public:
// 发布新闻,状态变更并推送通知
void publishNews(const string& news)
{
currentNews = news;
cout << "\n【新闻平台发布新资讯】:" << currentNews << endl;
notifyAll(currentNews);
}
};
3. 多种具体观察者
cpp
// 邮箱订阅用户
class EmailUser : public Observer
{
private:
string userName;
public:
EmailUser(string name) : userName(name) {}
void update(const string& newsInfo) override
{
cout << "邮箱用户[" << userName << "] 收到资讯:" << newsInfo << endl;
}
};
// 手机短信订阅用户
class SmsUser : public Observer
{
private:
string phoneNum;
public:
SmsUser(string phone) : phoneNum(phone) {}
void update(const string& newsInfo) override
{
cout << "短信用户[" << phoneNum << "] 收到资讯:" << newsInfo << endl;
}
};
// 移动端APP订阅用户
class AppUser : public Observer
{
private:
string userId;
public:
AppUser(string id) : userId(id) {}
void update(const string& newsInfo) override
{
cout << "APP用户[" << userId << "] 收到资讯:" << newsInfo << endl;
}
};
4. 客户端业务调用
cpp
int main()
{
// 创建发布主题
NewsSubject newsPlatform;
// 创建各类观察者用户
EmailUser user1("李四");
SmsUser user2("15612345678");
AppUser user3("user001");
// 全部订阅平台资讯
newsPlatform.attach(&user1);
newsPlatform.attach(&user2);
newsPlatform.attach(&user3);
// 第一次发布新闻,全员接收
newsPlatform.publishNews("C++观察者模式实战教程正式上线");
// 取消短信用户订阅
newsPlatform.detach(&user2);
cout << "\n===== 已取消短信用户订阅 =====" << endl;
// 第二次发布新闻,仅剩余用户接收
newsPlatform.publishNews("设计模式全套学习资料更新完成");
return 0;
}
六、实战业务案例:股票价格监听系统
cpp
// 股票实体类
class Stock
{
private:
string stockCode;
double stockPrice;
public:
Stock(string code, double price) : stockCode(code), stockPrice(price) {}
void setPrice(double price) { stockPrice = price; }
double getPrice() const { return stockPrice; }
string getCode() const { return stockCode; }
};
// 股票交易市场(具体主题)
class StockMarket : public Subject
{
private:
Stock stockData;
public:
StockMarket(string code, double price) : stockData(code, price) {}
// 股票价格变动
void changePrice(double newPrice)
{
stockData.setPrice(newPrice);
string msg = "股票[" + stockData.getCode() + "]最新价格:" + to_string(newPrice);
notifyAll(msg);
}
};
// 股民观察者
class StockInvestor : public Observer
{
private:
string investorName;
public:
StockInvestor(string name) : investorName(name) {}
void update(const string& msg) override
{
cout << "股民[" << investorName << "] 实时接收行情:" << msg << endl;
}
};
七、C++ 现代优雅写法(函数式轻量观察者)
无需继承类,使用std::function实现极简事件监听,适合快速开发
cpp
#include <functional>
#include <unordered_map>
#include <mutex>
// 轻量级事件分发器
template<typename MsgType>
class EventDispatcher
{
private:
using EventFunc = function<void(const MsgType&)>;
unordered_map<uint64_t, EventFunc> eventMap;
mutable mutex mtx;
uint64_t eventId = 1;
public:
// 注册监听事件
uint64_t registerEvent(EventFunc&& func)
{
lock_guard<mutex> lock(mtx);
uint64_t id = eventId++;
eventMap[id] = move(func);
return id;
}
// 移除监听事件
void removeEvent(uint64_t id)
{
lock_guard<mutex> lock(mtx);
eventMap.erase(id);
}
// 推送事件消息
void pushEvent(const MsgType& msg)
{
lock_guard<mutex> lock(mtx);
auto tempMap = eventMap;
for (auto& pair : tempMap)
{
if (pair.second) pair.second(msg);
}
}
};
// 使用示例
int main()
{
EventDispatcher<string> dispatcher;
auto id1 = dispatcher.registerEvent([](const string& info){
cout << "监听1:" << info << endl;
});
auto id2 = dispatcher.registerEvent([](const string& info){
cout << "监听2:" << info << endl;
});
dispatcher.pushEvent("函数式观察者模式执行成功");
dispatcher.removeEvent(id2);
dispatcher.pushEvent("仅保留监听1生效");
return 0;
}
八、C++ 项目开发最佳实践
-
项目目录分层规范
ObserverPattern/
├─ ObserverBase.h // 抽象观察者基类
├─ SubjectBase.h // 抽象主题基类
├─ ConcreteSubject.h // 业务具体主题
└─ ConcreteObserver.h// 各类业务观察者 -
严格区分事件生产者 与事件消费者,业务逻辑互不侵入
-
界面UI场景优先使用传统继承式观察者,后台服务优先使用函数式事件分发
-
订阅关系随对象生命周期绑定,对象销毁前必须主动取消订阅
-
批量事件通知拆分优先级队列,核心事件优先推送,非核心事件延后执行
-
禁止在观察者更新函数中修改主题状态,从源头规避循环通知问题
-
日志、监控、告警等通用统一监听业务,封装全局静态事件分发器
九、模式核心总结
观察者模式核心精髓:一对多绑定状态,订阅监听解耦业务,状态变更自动广播
该模式是C++行为型模式中使用频率最高的设计模式之一,广泛应用于界面交互、事件驱动、数据同步、消息推送等场景,依靠订阅-通知机制彻底解决多对象状态联动耦合问题,代码结构清晰、拓展便捷,是实现事件驱动架构的核心基础模式。