C++设计模式:观察者模式
观察者模式
观察者模式(Observer Pattern)是软件设计模式中行为型模式的典型代表,它在解耦对象间依赖、实现事件通知机制方面非常有用。我们来深入理解它,帮助你真正掌握。
一、核心思想:一对多的依赖关系
想象一下你订阅了某个公众号。当公众号发布新文章时,所有订阅者都会收到推送。公众号是被观察的对象,订阅者是观察者。
这就是观察者模式的核心:
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
- 一个被观察者 (Subject/Observable):也叫“主题”或“发布者”,维护一个观察者列表。
- 多个观察者 (Observer):也叫“订阅者”,当被观察者状态改变时,会收到通知。
二、角色与结构 (UML 简化)
-
Subject(抽象被观察者)- 定义管理观察者的方法:
attach(Observer*),detach(Observer*)。 - 定义通知观察者的方法:
notify()。 - 通常持有一个
std::vector<Observer*>来存储观察者。
- 定义管理观察者的方法:
-
ConcreteSubject(具体被观察者)- 继承
Subject。 - 拥有具体的状态(
state)。 - 当状态改变时,调用
notify()方法通知所有观察者。
- 继承
-
Observer(抽象观察者)- 定义一个更新接口
update(),所有具体观察者必须实现。
- 定义一个更新接口
-
ConcreteObserver(具体观察者)- 继承
Observer。 - 实现
update()方法,定义收到通知后的具体行为。 - 通常会持有对
ConcreteSubject的引用,以便获取最新状态。
- 继承
三、C++ 代码实现 (经典版)
#include <iostream>
#include <vector>
#include <algorithm>
// 1. 抽象观察者
class Observer {
public:
virtual ~Observer() = default;
virtual void update() = 0; // 纯虚函数,由子类实现
};
// 2. 抽象被观察者
class Subject {
public:
virtual ~Subject() = default;
// 添加观察者
void attach(Observer* observer) {
observers.push_back(observer);
}
// 移除观察者
void detach(Observer* observer) {
observers.erase(
std::remove(observers.begin(), observers.end(), observer),
observers.end()
);
}
// 通知所有观察者
void notify() {
for (auto* obs : observers) {
obs->update(); // 调用每个观察者的 update
}
}
private:
std::vector<Observer*> observers; // 观察者列表
};
// 3. 具体被观察者 (例如:天气数据发布器)
class WeatherData : public Subject {
public:
void setMeasurements(float temperature, float humidity) {
this->temperature = temperature;
this->humidity = humidity;
measurementsChanged(); // 状态改变,触发通知
}
// 获取状态的方法,供观察者调用
float getTemperature() const { return temperature; }
float getHumidity() const { return humidity; }
private:
float temperature;
float humidity;
// 状态改变时调用
void measurementsChanged() {
notify(); // 通知所有观察者
}
};
// 4. 具体观察者A:当前状况显示
class CurrentConditionsDisplay : public Observer {
public:
// 必须持有对被观察者的引用,以便获取数据
explicit CurrentConditionsDisplay(WeatherData* weatherData)
: weatherData(weatherData) {
weatherData->attach(this); // 注册自己为观察者
}
~CurrentConditionsDisplay() override {
weatherData->detach(this); // 析构时反注册
}
void update() override {
float temp = weatherData->getTemperature();
float hum = weatherData->getHumidity();
std::cout << "【当前状况】温度: " << temp
<< "°C, 湿度: " << hum << "%\n";
}
private:
WeatherData* weatherData; // 依赖具体被观察者 (稍弱)
};
// 5. 具体观察者B:统计显示
class StatisticsDisplay : public Observer {
public:
explicit StatisticsDisplay(WeatherData* weatherData)
: weatherData(weatherData), maxTemp(-100), minTemp(100), tempSum(0), numReadings(0) {
weatherData->attach(this);
}
~StatisticsDisplay() override {
weatherData->detach(this);
}
void update() override {
float temp = weatherData->getTemperature();
tempSum += temp;
numReadings++;
maxTemp = std::max(maxTemp, temp);
minTemp = std::min(minTemp, temp);
std::cout << "【统计】平均: " << (tempSum / numReadings)
<< "°C, 最高: " << maxTemp << "°C, 最低: " << minTemp << "°C\n";
}
private:
WeatherData* weatherData;
float maxTemp, minTemp, tempSum;
int numReadings;
};
// --- 使用示例 ---
int main() {
WeatherData weatherData;
// 创建观察者,它们会自动注册
CurrentConditionsDisplay currentDisplay(&weatherData);
StatisticsDisplay statsDisplay(&weatherData);
// 模拟数据更新
std::cout << "第一次更新:\n";
weatherData.setMeasurements(25.0f, 65.0f);
std::cout << "\n第二次更新:\n";
weatherData.setMeasurements(28.0f, 70.0f);
std::cout << "\n第三次更新:\n";
weatherData.setMeasurements(22.0f, 55.0f);
return 0;
}
输出:
第一次更新:
【当前状况】温度: 25°C, 湿度: 65%
【统计】平均: 25°C, 最高: 25°C, 最低: 25°C
第二次更新:
【当前状况】温度: 28°C, 湿度: 70%
【统计】平均: 26.5°C, 最高: 28°C, 最低: 25°C
第三次更新:
【当前状况】温度: 22°C, 湿度: 55%
【统计】平均: 25°C, 最高: 28°C, 最低: 22°C
四、深入理解关键点
-
松耦合 (Loose Coupling)
- Subject 只知道 Observer 接口:Subject 不关心具体是哪个观察者,只要实现了
update()就行。这使得可以轻松添加新的观察者类型,而无需修改 Subject。 - 观察者依赖具体 Subject:在经典实现中,
ConcreteObserver通常依赖ConcreteSubject来获取数据。这是一个弱点。理想情况下,update()应该通过参数传递数据。
- Subject 只知道 Observer 接口:Subject 不关心具体是哪个观察者,只要实现了
-
推模型 vs 拉模型 (Push vs Pull)
- 拉模型 (Pull):如上例,
update()无参数,观察者主动从 Subject 拉取所需数据。优点是灵活,缺点是观察者需要知道 Subject 的细节。 - 推模型 (Push):
notify()调用update(data),将变化的数据直接推送给观察者。优点是高效、解耦更好(观察者无需知道 Subject 细节),缺点是如果数据过多或观察者不需要全部数据,可能浪费。
改进建议:使用推模型,让
update()接收必要的数据参数。 - 拉模型 (Pull):如上例,
-
注册与反注册
- 观察者必须在生命周期结束前从 Subject 反注册 (
detach),否则 Subject 持有的指针会变成悬空指针,导致崩溃。 - 在 C++ 中,通常在观察者的析构函数中调用
detach。
- 观察者必须在生命周期结束前从 Subject 反注册 (
-
通知顺序
notify()通常按注册顺序通知。如果观察者之间有依赖关系(如一个观察者的结果影响另一个),需要小心处理。
-
性能考虑
- 如果观察者很多,
notify()可能成为性能瓶颈。 - 可以考虑异步通知、批处理或只通知状态真正改变时。
- 如果观察者很多,
五、现代 C++ 改进 (推荐)
为了更安全、更灵活,可以使用智能指针和函数对象:
#include <iostream>
#include <vector>
#include <memory>
#include <functional>
class WeatherData {
public:
using UpdateCallback = std::function<void(float, float)>;
void addObserver(const UpdateCallback& callback) {
callbacks.push_back(callback);
}
void removeObserver(const UpdateCallback& callback) {
// 实际中可能需要更复杂的标识机制
// 这里简化处理,通常用句柄或ID
}
void setMeasurements(float temperature, float humidity) {
this->temperature = temperature;
this->humidity = humidity;
notify();
}
private:
void notify() {
for (const auto& cb : callbacks) {
cb(temperature, humidity); // 推模型,直接传递数据
}
}
std::vector<UpdateCallback> callbacks;
float temperature, humidity;
};
int main() {
WeatherData weatherData;
// 使用 Lambda 作为观察者 (更灵活)
auto currentDisplay = [](float temp, float hum) {
std::cout << "【Lambda 显示】温度: " << temp << "°C, 湿度: " << hum << "%\n";
};
weatherData.addObserver(currentDisplay);
weatherData.setMeasurements(30.0f, 80.0f);
return 0;
}
优点:
- 类型安全:使用
std::function。 - 自动内存管理:配合
std::shared_ptr可避免悬空指针。 - 高度灵活:可注册函数、Lambda、成员函数等。
六、应用场景
- GUI 事件系统:按钮点击,通知所有监听器。
- MVC 架构:Model (Subject) 状态改变,通知 View (Observer) 更新。
- 消息队列/发布-订阅系统。
- 游戏开发:玩家死亡事件,通知 UI、音效、AI 等模块。
- 数据监控与日志。
七、总结
观察者模式的强大之处在于解耦和动态扩展。你掌握了它,就能设计出更灵活、更易维护的系统。
学习建议:
- 先理解经典实现的四个角色。
- 动手敲一遍代码,体会注册、通知、更新的流程。
- 思考“推”和“拉”的区别,并尝试修改代码实现推模型。
- 用现代 C++ 特性重构,体会其优势。
- 在自己的项目中寻找可以应用观察者模式的场景。
观察者模式 有哪些常见应用场景
观察者模式(Observer Pattern)因其强大的解耦能力和事件驱动特性,在软件开发中应用极为广泛。它非常适合“当一个对象状态改变,需要通知其他多个对象”的场景。
以下是观察者模式的一些常见应用场景,涵盖桌面应用、Web开发、游戏、系统设计等多个领域:
1. 图形用户界面 (GUI) 事件处理
这是最经典的应用。
- 场景:用户点击按钮、输入文本、选择菜单项。
- 实现:
Subject:按钮(Button)、文本框(TextBox)等控件。Observer:事件处理器(如onClick(),onTextChanged())。
- 例子:
- 一个“提交”按钮被点击时,需要通知“表单验证器”进行验证,并通知“数据保存器”保存数据。
- 滑动条(Slider)值改变时,通知多个显示组件更新数值。
2. 模型-视图-控制器 (MVC) 架构
这是观察者模式的教科书级应用。
- 场景:数据(Model)改变时,自动更新用户界面(View)。
- 实现:
Subject:Model(数据模型)。Observer:View(视图组件)。
- 例子:
- 在股票行情软件中,股票价格(Model)实时变动,多个图表和列表(View)自动刷新显示最新价格。
- 文档编辑器中,文档内容改变,状态栏、预览窗格等视图同步更新。
3. 发布-订阅系统 (Pub-Sub)
观察者模式是发布-订阅模式的基础。
- 场景:消息中间件、事件总线。
- 实现:
Subject:消息主题(Topic)或事件总线(Event Bus)。Observer:订阅者(Subscriber)。
- 例子:
- 微服务架构中,订单服务发布“订单创建”事件,库存服务、物流服务、通知服务等订阅该事件并执行相应逻辑。
- 游戏中的事件系统:
EventManager发布“玩家死亡”事件,音效系统、UI系统、AI系统等订阅并响应。
4. 游戏开发
游戏逻辑复杂,事件繁多,观察者模式非常适用。
- 场景:游戏状态变化、角色行为、UI更新。
- 例子:
- 玩家生命值归零 → 通知“死亡动画播放器”、“得分系统”、“游戏结束UI”。
- 收集到金币 → 通知“音效系统”播放音效、“UI系统”更新金币计数、“成就系统”检查成就。
- 游戏暂停 → 通知所有活动的游戏对象(敌人、主角、特效)暂停。
5. 数据绑定与响应式编程
现代前端框架的核心思想。
- 场景:数据模型与UI的自动同步。
- 实现:
Subject:响应式数据变量(如 Vue 的data,React 的state)。Observer:依赖该数据的 UI 组件或计算属性。
- 例子:
- 在 Vue.js 中,
data中的message改变时,所有使用{{ message }}的模板自动更新。 - RxJS 中的
Observable和Observer,实现数据流的监听与响应。
- 在 Vue.js 中,
6. 日志与监控系统
- 场景:系统发生关键事件时,需要记录日志、发送告警、更新监控面板。
- 例子:
- 系统出现错误 → 通知“日志记录器”写入日志、“邮件告警器”发送邮件、“监控仪表盘”显示错误数。
- 服务器CPU使用率超过阈值 → 通知“告警系统”、“自动扩容模块”。
7. 配置管理
- 场景:应用配置动态更新,需要通知所有依赖该配置的模块。
- 例子:
- 用户修改了“主题颜色”配置 → 通知所有UI组件重新加载主题。
- 后台管理界面修改了“最大连接数” → 通知数据库连接池调整大小。
8. 财务与交易系统
- 场景:价格变动、交易达成、订单状态更新。
- 例子:
- 股票价格变动 → 通知所有持有该股票的用户的交易界面、风险控制系统、算法交易模块。
- 订单状态从“待支付”变为“已支付” → 通知库存系统扣减库存、物流系统准备发货、用户通知系统发送短信。
9. 多人协作应用
- 场景:一个用户的操作需要实时同步给其他用户。
- 例子:
- 在线文档编辑(如 Google Docs):用户A输入文字 → 通过服务器广播 → 用户B、C的文档视图自动更新。
- 协同白板:用户A画了一条线 → 其他用户的白板上同步出现这条线。
10. 缓存失效
- 场景:数据源更新时,需要使相关缓存失效。
- 例子:
- 数据库中用户信息更新 → 通知“缓存服务”删除该用户的缓存数据。
总结:何时使用观察者模式?
当你遇到以下情况时,可以考虑使用观察者模式:
- 一个对象的改变需要同时改变其他多个对象。
- 对象之间耦合度要尽可能低(发布者不关心谁订阅,订阅者不关心谁发布)。
- 需要支持动态添加或移除“监听者”。
- 事件驱动的系统,需要对特定事件做出反应。
关键优势:
- 松耦合:Subject 和 Observer 之间是抽象耦合。
- 可扩展性:可以轻松添加新的 Observer,无需修改 Subject。
- 支持广播通信:一个 Subject 可以通知多个 Observer。
掌握这些应用场景,你就能在实际项目中灵活运用观察者模式,设计出更优雅、更健壮的软件系统。
有哪些经典案例使用了观察者模式
观察者模式是软件设计中最基础、最广泛使用的模式之一。许多经典框架、库和系统都深度应用了观察者模式,它往往是这些系统能够实现高内聚、低耦合和事件驱动架构的关键。
以下是几个极具代表性的经典案例,它们不仅在技术上应用了观察者模式,更在软件工程史上具有里程碑意义:
1. Java AWT/Swing 事件模型 (GUI 事件系统)
- 地位:这是观察者模式最著名、最权威的官方应用案例之一。
- 实现方式:
java.util.Observable类(被观察者)java.util.Observer接口(观察者)
- 经典场景:在 Java Swing 桌面应用中,用户点击一个按钮(
JButton)。- Subject (被观察者):
JButton对象。 - Observer (观察者):实现了
ActionListener接口的类(如MyActionListener)。 - 注册:
button.addActionListener(myListener); - 通知:当按钮被点击时,
button会调用myListener.actionPerformed(event)。
- Subject (被观察者):
- 意义:它确立了“组件-监听器”这一 GUI 编程范式,后续几乎所有 GUI 框架(包括 C# WinForms、Android、Qt 等)都借鉴了这一思想。
2. .NET Framework 事件与委托 (Delegates and Events)
- 地位:C# 语言级支持的观察者模式,极其优雅和高效。
- 实现方式:
- 使用
delegate(委托)定义回调方法的签名。 - 使用
event关键字声明事件,它是对委托的封装,提供+=(订阅) 和-=(取消订阅) 操作。
- 使用
- 经典场景:Windows Forms 或 WPF 应用中的按钮点击。
// 定义事件 public event EventHandler<MyEventArgs> DataChanged; // 触发事件(通知观察者) protected virtual void OnDataChanged(MyEventArgs e) { DataChanged?.Invoke(this, e); } // 使用 myObject.DataChanged += MyHandler; // 订阅 - 意义:.NET 将观察者模式内化为语言特性,使得事件处理简洁、类型安全,是现代事件驱动编程的典范。
3. Spring Framework 事件发布 (ApplicationEvent & ApplicationListener)
- 地位:企业级 Java 开发中解耦业务逻辑的经典实践。
- 实现方式:
ApplicationEventPublisher(被观察者/发布者)ApplicationListener<T>(观察者/监听者)
- 经典场景:用户注册后,需要发送欢迎邮件、初始化用户数据、记录日志。
// 1. 发布事件 applicationEventPublisher.publishEvent(new UserRegisteredEvent(userId)); // 2. 监听事件 @EventListener public void handleUserRegistration(UserRegisteredEvent event) { sendWelcomeEmail(event.getUserId()); } - 意义:避免了在“用户注册”主流程中硬编码所有后续操作,实现了业务逻辑的解耦和可扩展性。新增一个监听器(如发短信)无需修改注册代码。
4. RxJava / ReactiveX (响应式编程库)
- 地位:将观察者模式提升到“数据流”层面的集大成者。
- 实现方式:
Observable(被观察者):代表一个可以发射数据流的源头。Observer(观察者):接收并处理数据流。- 核心是
subscribe(Observer)方法。
- 经典场景:处理异步数据流,如网络请求、用户输入、传感器数据。
Observable.fromCallable(() -> fetchDataFromNetwork()) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<String>() { @Override public void onNext(String data) { // 处理数据 (观察者收到通知) } }); - 意义:它不仅仅是简单的“状态改变通知”,而是将一切视为流(Everything is a stream),通过组合、转换、过滤等操作符构建复杂的数据处理管道,是现代异步编程的基石。
5. Vue.js / React 等前端框架 (响应式数据绑定)
- 地位:现代前端框架的核心机制。
- 实现方式:
- Vue.js:使用
Object.defineProperty或Proxy拦截数据的get和set。在get时收集依赖(订阅),在set时触发更新(通知)。 - React:虽然机制不同(基于状态更新触发
render),但其useState和useEffect的思想与观察者模式高度相似。useEffect可以看作是“观察”某些状态的变化。
- Vue.js:使用
- 经典场景:模板中
{{ message }}显示的数据。- 当 JavaScript 中的
message变量改变时,DOM 自动更新。
- 当 JavaScript 中的
- 意义:开发者无需手动操作 DOM,只需关注数据,框架自动处理视图更新,极大提升了开发效率和代码可维护性。
6. Qt 框架的信号与槽 (Signals and Slots)
- 地位:C++ GUI 开发中观察者模式的优雅实现。
- 实现方式:
signals:被观察者发出的信号。slots:观察者提供的槽函数,用于响应信号。- 使用
connect()函数建立连接。
- 经典场景:按钮点击触发动作。
connect(button, &QPushButton::clicked, this, &MyClass::handleClick); - 意义:提供了类型安全、松耦合的对象间通信机制,是 Qt 框架的核心特色之一。
总结
这些经典案例的共同点是:
- 解耦:发布者(Subject)不关心谁订阅,订阅者(Observer)不关心谁发布。
- 可扩展:可以动态添加新的观察者,无需修改发布者代码。
- 事件驱动:系统围绕“事件”构建,逻辑清晰。
学习这些经典案例,不仅能理解观察者模式的应用,更能体会到优秀软件设计的思想。你可以从 Java Swing 或 .NET 的事件模型开始,它们是理解该模式最直观的入口。
观察者模式在实际项目中如何优化?
在实际项目中,直接使用“教科书式”的观察者模式可能会遇到性能、内存安全、可维护性等问题。因此,对其进行优化至关重要。
以下是针对不同痛点的实用优化策略,帮助你在真实项目中更高效、更安全地应用观察者模式:
1. 内存安全与生命周期管理 (C++ 尤其重要)
问题:经典实现中,Subject 持有 Observer* 原始指针。如果 Observer 对象被销毁而未从 Subject 反注册,Subject 的指针会变成悬空指针(Dangling Pointer),再次调用 notify() 会导致程序崩溃。
优化方案:
-
使用智能指针 + 弱引用 (Weak Reference)
#include <memory> #include <vector> class Observer { public: virtual ~Observer() = default; virtual void update() = 0; }; class Subject { public: // 使用 weak_ptr 避免循环引用和悬空指针 void attach(std::weak_ptr<Observer> observer) { observers.push_back(observer); } void notify() { // 过滤掉已销毁的观察者 observers.erase( std::remove_if(observers.begin(), observers.end(), [](const std::weak_ptr<Observer>& wp) { return wp.expired(); }), observers.end() ); for (auto& wp : observers) { if (auto sp = wp.lock()) { // 获取 shared_ptr sp->update(); } } } private: std::vector<std::weak_ptr<Observer>> observers; };- 优点:自动处理对象销毁,无需手动
detach。 - 缺点:每次通知都需要遍历并检查
expired(),稍有性能开销。
- 优点:自动处理对象销毁,无需手动
-
提供自动反注册机制 在
Observer的析构函数中自动调用detach():class ConcreteObserver : public Observer { public: ConcreteObserver(Subject* s) : subject(s) { subject->attach(this); } ~ConcreteObserver() override { subject->detach(this); } // 自动反注册 void update() override { /* ... */ } private: Subject* subject; };- 注意:确保
Subject的生命周期长于Observer,否则仍可能出问题。
- 注意:确保
2. 性能优化
问题:当观察者数量庞大或 notify() 调用频繁时,遍历所有观察者并调用 update() 可能成为性能瓶颈。
优化方案:
-
避免无意义的通知 (Change Detection) 只有在状态真正改变时才通知。
void WeatherData::setMeasurements(float temp, float hum) { if (this->temperature == temp && this->humidity == hum) { return; // 状态未变,不通知 } this->temperature = temp; this->humidity = hum; notify(); } -
异步通知 (Async Notification) 将通知放入工作队列,由后台线程处理,避免阻塞主线程(尤其在 GUI 或游戏主循环中)。
void Subject::notifyAsync() { std::thread([observers = this->observers]() { for (auto* obs : observers) { obs->update(); // 在后台线程执行 } }).detach(); }- 注意:需考虑线程安全和数据同步。
-
批量更新 (Batch Updates) 在一帧或一个事件周期内,只通知一次,而不是每次状态微小变化都通知。
- 例子:游戏引擎中,将所有“实体位置变化”事件收集起来,每帧末尾统一通知渲染系统。
3. 解耦优化:推模型 vs 拉模型
问题:经典“拉模型”要求 Observer 知道 ConcreteSubject 的细节来获取数据,耦合度高。
优化方案:
- 采用“推模型” (Push Model) 将变化的数据作为参数传递给
update()。class Observer { public: virtual void update(float temperature, float humidity) = 0; // 或传递一个数据包 // virtual void update(const SensorData& data) = 0; }; void WeatherData::notify() { for (auto* obs : observers) { obs->update(temperature, humidity); // 推送数据 } }- 优点:
Observer无需知道WeatherData,解耦更好;减少对Subject的依赖调用。
- 优点:
4. 灵活性优化:使用函数对象 (Function Objects)
问题:继承 Observer 接口不够灵活,尤其是对于简单的回调。
优化方案:
- 使用
std::function和 Lambda#include <functional> #include <vector> class Subject { public: using Callback = std::function<void(float, float)>; void addObserver(Callback cb) { callbacks.push_back(cb); } void notify(float temp, float hum) { for (const auto& cb : callbacks) { cb(temp, hum); // 调用函数对象 } } private: std::vector<Callback> callbacks; }; // 使用示例 Subject subject; subject.addObserver([](float t, float h) { std::cout << "温度: " << t << ", 湿度: " << h << "\n"; });- 优点:支持函数、Lambda、成员函数绑定(
std::bind),极其灵活。 - 结合智能指针:可以返回一个“句柄”(Handle),用于取消订阅。
- 优点:支持函数、Lambda、成员函数绑定(
5. 功能增强:过滤与选择性通知
问题:并非所有观察者都关心所有类型的通知。
优化方案:
- 基于事件类型的通知
enum class EventType { TEMPERATURE_CHANGED, HUMIDITY_CHANGED, PRESSURE_CHANGED }; class Subject { public: void notify(EventType type, float value) { // 只通知对该事件类型感兴趣的观察者 for (auto& [callback, eventTypes] : listeners) { if (eventTypes.count(type)) { callback(type, value); } } } void addListener(Callback cb, const std::set<EventType>& events) { listeners.emplace_back(cb, events); } };
6. 调试与监控
- 添加日志:在
attach,detach,notify时打印日志,便于调试。 - 监控观察者数量:提供接口查询当前观察者数量,防止内存泄漏。
总结:实际项目中的最佳实践
| 优化目标 | 推荐方案 |
|---|---|
| 内存安全 | 使用 std::weak_ptr 或确保自动反注册 |
| 高性能 | 变化检测、异步通知、批量更新 |
| 高解耦 | 采用“推模型”,避免观察者主动拉取数据 |
| 高灵活性 | 使用 std::function + Lambda |
| 易用性 | 提供清晰的 API,支持按事件类型过滤 |
核心思想:不要拘泥于“四角色”的经典结构。在现代 C++ 中,以“事件总线”或“信号-槽” 的思想来理解和实现观察者模式,往往更符合实际需求。
通过这些优化,你的观察者模式实现将更加健壮、高效,能够胜任复杂项目的要求。
更多推荐


所有评论(0)