注意:复现代码时,确保 VS2022 使用 C++17/20 标准以支持现代特性。
事件驱动的订阅通知机制
1. 模式定义与用途
核心思想
- 观察者模式 :定义一种一对多依赖关系 ,当一个对象(主题)状态改变时,所有依赖它的对象(观察者)自动收到通知并更新 。
- 关键用途 :
1.解耦发布者与订阅者 :主题无需知道观察者的具体实现。
2.实时通知 :支持动态添加或移除观察者。
3.事件驱动架构:适用于GUI事件处理、数据监控、消息队列等场景。
- 关键用途 :
经典场景
- 股票价格变动通知多个交易终端。
- 用户界面控件状态同步(如按钮点击触发面板更新)。
- 传感器数据监控与报警系统。
2. 模式结构解析
UML类图
plaintext
+---------------------+ +---------------------+
| Subject | | Observer |
+---------------------+ +---------------------+
| + attach(o: Observer)| | + update(): void |
| + detach(o: Observer)| +---------------------+
| + notify() | ^
+---------------------+ |
^ |
| +-------+-------+
| | |
+---------------------+ +-------------------+ +-------------------+
| ConcreteSubject | | ConcreteObserverA | | ConcreteObserverB |
+---------------------+ +-------------------+ +-------------------+
| + getState() | | + update() | | + update() |
| + setState() | +-------------------+ +-------------------+
+---------------------+
角色说明
Subject
:主题接口,管理观察者列表,提供订阅/取消订阅方法。ConcreteSubject
:具体主题,存储状态并在状态变化时通知观察者。Observer
:观察者接口,定义更新方法update()
。ConcreteObserver
:具体观察者,实现更新逻辑。
3. 现代C++实现示例
场景:股票价格变动通知系统
步骤1:定义主题与观察者接口
cpp
#include <iostream>
#include <vector>
#include <memory>
#include <unordered_set>
// 前向声明
class StockObserver;
// 主题接口
class StockSubject {
public:
virtual ~StockSubject() = default;
virtual void attach(std::shared_ptr<StockObserver> observer) = 0;
virtual void detach(std::shared_ptr<StockObserver> observer) = 0;
virtual void notify() = 0;
};
// 观察者接口
class StockObserver : public std::enable_shared_from_this<StockObserver> {
public:
virtual ~StockObserver() = default;
virtual void update(double price) = 0;
};
步骤2:实现具体主题(股票)
cpp
class Stock : public StockSubject {
public:
Stock(const std::string& symbol) : symbol_(symbol), price_(0.0) {}
void setPrice(double price) {
price_ = price;
notify();
}
void attach(std::shared_ptr<StockObserver> observer) override {
observers_.insert(observer);
}
void detach(std::shared_ptr<StockObserver> observer) override {
observers_.erase(observer);
}
void notify() override {
for (auto& observer : observers_) {
observer->update(price_);
}
}
private:
std::string symbol_;
double price_;
std::unordered_set<std::shared_ptr<StockObserver>> observers_;
};
步骤3:实现具体观察者(交易终端)
cpp
class TradingTerminal : public StockObserver {
public:
TradingTerminal(const std::string& name) : name_(name) {}
void update(double price) override {
std::cout << "终端 [" << name_ << "] 收到报价: $" << price << "\n";
if (price > alertThreshold_) {
std::cout << "警告:价格超过阈值!\n";
}
}
void setAlertThreshold(double threshold) {
alertThreshold_ = threshold;
}
private:
std::string name_;
double alertThreshold_ = 100.0;
};
步骤4:客户端代码
cpp
int main() {
// 创建股票与观察者
auto appleStock = std::make_shared<Stock>("AAPL");
auto terminal1 = std::make_shared<TradingTerminal>("终端1");
auto terminal2 = std::make_shared<TradingTerminal>("终端2");
terminal2->setAlertThreshold(150.0);
// 订阅股票价格
appleStock->attach(terminal1);
appleStock->attach(terminal2);
// 更新价格并触发通知
appleStock->setPrice(120.5);
/* 输出:
终端 [终端1] 收到报价: $120.5
警告:价格超过阈值!
终端 [终端2] 收到报价: $120.5
*/
// 取消订阅终端2
appleStock->detach(terminal2);
appleStock->setPrice(160.0);
/* 输出:
终端 [终端1] 收到报价: $160
警告:价格超过阈值!
*/
}
4. 应用场景示例
场景1:GUI事件处理
cpp
class Button {
public:
void addListener(std::shared_ptr<ButtonListener> listener) {
listeners_.push_back(listener);
}
void click() {
// 触发点击事件
for (auto& listener : listeners_) {
listener->onClick();
}
}
private:
std::vector<std::shared_ptr<ButtonListener>> listeners_;
};
class LoggingListener : public ButtonListener {
public:
void onClick() override {
std::cout << "按钮被点击,记录日志\n";
}
};
场景2:气象站数据监控
cpp
class WeatherStation : public Subject {
public:
void setTemperature(double temp) {
temperature_ = temp;
notify();
}
};
class PhoneApp : public Observer {
public:
void update(double temp) override {
std::cout << "手机APP更新温度: " << temp << "°C\n";
}
};
5. 优缺点分析
优点 | 缺点 |
---|---|
松耦合设计,主题与观察者独立演化 | 观察者可能收到不相关通知(需过滤逻辑) |
支持动态订阅机制 | 链式触发可能导致性能问题(级联更新) |
简化事件分发逻辑 | 需注意内存泄漏(观察者未正确注销) |
6. 调试与优化策略
调试技巧(VS2022)
1. 验证通知触发:
- 在
notify()
方法中设置断点,检查观察者列表是否正确更新。
2. 跟踪观察者状态:
- 使用内存诊断工具确保观察者对象在注销后被正确释放。
性能优化
1. 批量通知:
- 合并多次状态变更,延迟发送通知(如防抖处理)。
cpp
void Stock::setPrice(double price) {
price_ = price;
if (needNotify_) { // 防抖逻辑
notify();
needNotify_ = false;
}
}
2. 异步通知:
cpp
#include <future>
void Stock::notify() {
std::async(std::launch::async, [this] {
for (auto& observer : observers_) {
observer->update(price_);
}
});
}