设计模式(C++)详解——观察者模式(Observer)(1)
观察者模式(Observer)深度解析:从理论到C++实战
1. 背景与核心概念
1.1 设计模式的起源与发展
观察者模式是软件工程中最经典、最常用的设计模式之一,它的历史可以追溯到面向对象编程的早期阶段。让我们回顾一下它的发展历程:
时期 | 主要贡献 | 应用场景 | 技术特点 |
---|---|---|---|
1970s | Smalltalk MVC框架 | GUI应用程序 | 模型-视图分离 |
1994 | GoF《设计模式》 | 事件处理系统 | 标准化模式描述 |
2000s | .NET事件委托 | 桌面应用开发 | 语言级支持 |
2010s | 响应式编程 | Web前端、移动端 | 数据流驱动 |
观察者模式的核心思想源于现实世界中的订阅-发布机制。就像报纸订阅一样:
- 出版社(主题)维护订阅者列表
- 读者(观察者)可以订阅或取消订阅
- 新报纸出版时,所有订阅者自动收到通知
1.2 模式定义与关键角色
观察者模式:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
让我们用Mermaid类图来理解各个角色的关系:
关键角色解析:
- Subject(主题):维护观察者列表,提供添加、删除和通知观察者的接口
- ConcreteSubject(具体主题):存储感兴趣的状态,状态改变时通知观察者
- Observer(观察者):为所有具体观察者定义更新接口
- ConcreteObserver(具体观察者):维护与主题状态一致的自身状态
1.3 MVC架构中的核心地位
观察者模式是MVC(Model-View-Controller)架构的核心机制:
在这种架构中:
- Model 充当主题角色,维护业务数据和状态
- View 充当观察者角色,监听Model的变化并更新显示
- Controller 处理用户输入,修改Model状态
2. 设计意图与考量
2.1 核心设计目标
观察者模式的设计主要围绕以下几个目标:
1. 解耦性
- 主题不需要知道观察者的具体类
- 观察者可以独立变化和复用
- 降低系统各部分之间的依赖
2. 动态关系
- 运行时可以动态添加、删除观察者
- 观察者关系可以随时改变
- 支持灵活的订阅机制
3. 广播通信
- 支持一对多的通信模式
- 主题变化自动通知所有观察者
- 避免手动维护更新逻辑
2.2 生命周期管理的挑战
观察者模式中最棘手的问题就是生命周期管理。考虑以下场景:
// 危险代码示例:悬空指针问题
Subject* subject = new Subject();
Observer* observer = new ConcreteObserver();subject->attach(observer); // 主题持有观察者指针// 之后某个时刻...
delete observer; // 观察者被销毁
// 但主题仍然持有已销毁对象的指针!subject->notify(); // 灾难:访问已释放内存
解决方案对比:
方案 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
原始指针 | 简单直接,无额外开销 | 容易产生悬空指针 | 简单场景,生命周期明确 |
shared_ptr | 自动内存管理 | 循环引用导致内存泄漏 | 需要共享所有权的场景 |
weak_ptr | 避免循环引用,安全 | 使用稍复杂 | 观察者模式推荐方案 |
2.3 性能与扩展性权衡
通知性能考量:
- 观察者数量增加时,通知开销线性增长
- 可能需要支持按优先级通知
- 考虑异步通知机制避免阻塞
扩展性设计:
- 支持不同类型的观察者
- 允许观察者过滤感兴趣的事件
- 提供批量更新机制
3. 实例与应用场景
3.1 案例一:股票价格监控系统
这是一个典型的观察者模式应用,多个投资者监控同一支股票的价格变化。
#include <iostream>
#include <memory>
#include <vector>
#include <algorithm>
#include <string>
#include <unordered_map>// 前向声明
class StockSubject;/*** @brief 观察者接口* * 定义观察者必须实现的更新接口,用于接收主题状态变化的通知。*/
class StockObserver : public std::enable_shared_from_this<StockObserver> {
public:virtual ~StockObserver() = default;/*** @brief 更新方法* * 当观察的主题状态发生变化时,主题会调用此方法通知观察者。* * @in:* - subject: 发生状态变化的主题对象指针*/virtual void update(const StockSubject* subject) = 0;/*** @brief 获取观察者名称* * @return:观察者的标识名称*/virtual std::string getName() const = 0;
};/*** @brief 股票主题抽象基类* * 维护观察者列表,提供观察者的注册、注销和通知功能。*/
class StockSubject {
protected:std::string stock_symbol_; ///< 股票代码double current_price_; ///< 当前价格/*** @brief 观察者列表(使用weak_ptr避免循环引用)*/std::vector<std::weak_ptr<StockObserver>> observers_;public:/*** @brief 构造函数* * @in:* - symbol: 股票代码* - initial_price: 初始价格*/StockSubject(const std::string& symbol, double initial_price): stock_symbol_(symbol), current_price_(initial_price) {}virtual ~StockSubject() = default;/*** @brief 注册观察者* * 将观察者添加到观察者列表中,使其能够接收状态更新通知。* * @in:* - observer: 要注册的观察者共享指针*/void attach(const std::shared_ptr<StockObserver>& observer) {// 检查是否已经注册auto it = std::find_if(observers_.begin(), observers_.end(),[&observer](const std::weak_ptr<StockObserver>& wp) {return !wp.expired() && wp.lock() == observer;});if (it == observers_.end()) {observers_.emplace_back(observer);std::cout << "观察者 " << observer->getName() << " 开始监控股票 " << stock_symbol_ << std::endl;}}/*** @brief 注销观察者* * 从观察者列表中移除指定的观察者,使其不再接收通知。* * @in:* - observer: 要注销的观察者共享指针*/void detach(const std::shared_ptr<StockObserver>& observer) {observers_.erase(std::remove_if(observers_.begin(), observers_.end(),[&observer](const std::weak_ptr<StockObserver>& wp) {return wp.expired() || wp.lock() == observer;}),observers_.end());std::cout << "观察者 " << observer->getName() << " 停止监控股票 " << stock_symbol_ << std::endl;}/*** @brief 通知所有观察者* * 遍历观察者列表,调用每个观察者的update方法。* 自动清理已失效的观察者引用。*/void notify() {// 移除已失效的观察者observers_.erase(std::remove_if(observers_.begin(), observers_.end(),[](const std::weak_ptr<StockObserver>& wp) {return wp.expired();}),observers_.end());// 通知所有有效观察者for (auto& weak_observer : observers_) {if (auto observer = weak_observer.lock()) {observer->update(this);}}}/*** @brief 获取股票代码* * @return:股票代码字符串*/std::string getSymbol() const { return stock_symbol_; }/*** @brief 获取当前价格* * @return:当前股票价格*/double getPrice() const { return current_price_; }/*** @brief 获取观察者数量* * @return:当前注册的观察者数量*/size_t getObserverCount() const {return std::count_if(observers_.begin(), observers_.end(),[](const std::weak_ptr<StockObserver>& wp) {return !wp.expired();});}
};/*** @brief 具体股票主题* * 代表一支具体的股票,可以更新价格并通知所有观察者。*/
class ConcreteStock : public StockSubject {
public:/*** @brief 构造函数* * @in:* - symbol: 股票代码* - initial_price: 初始价格*/ConcreteStock(const std::string& symbol, double initial_price): StockSubject(symbol, initial_price) {}/*** @brief 更新股票价格* * 设置新的股票价格,如果价格发生变化则通知所有观察者。* * @in:* - new_price: 新的股票价格*/void setPrice(double new_price) {if (current_price_ != new_price) {double old_price = current_price_;current_price_ = new_price;std::cout << "\n股票 " << stock_symbol_ << " 价格变化: " << old_price << " -> " << new_price << std::endl;// 通知所有观察者notify();}}
};/*** @brief 投资者观察者* * 代表监控股票价格的投资者,在价格变化时执行相应的投资策略。*/
class Investor : public StockObserver {
private:std::string name_; ///< 投资者名称double buy_threshold_; ///< 买入阈值double sell_threshold_; ///< 卖出阈值std::string last_action_; ///< 上次操作记录public:/*** @brief 构造函数* * @in:* - name: 投资者名称* - buy_threshold: 价格低于此值时考虑买入* - sell_threshold: 价格高于此值时考虑卖出*/Investor(const std::string& name, double buy_threshold, double sell_threshold): name_(name), buy_threshold_(buy_threshold), sell_threshold_(sell_threshold), last_action_("无操作") {}/*** @brief 更新方法实现* * 接收股票价格变化通知,根据投资策略决定是否买入或卖出。* * @in:* - subject: 发生价格变化的股票主题*/void update(const StockSubject* subject) override {double current_price = subject->getPrice();const std::string& symbol = subject->getSymbol();std::cout << "[" << name_ << "] 收到通知: " << symbol << " 当前价格 = " << current_price << std::endl;// 投资策略决策if (current_price <= buy_threshold_) {last_action_ = "买入";std::cout << " → 决策: " << last_action_ << " " << symbol << " (价格 <= " << buy_threshold_ << ")" << std::endl;} else if (current_price >= sell_threshold_) {last_action_ = "卖出";std::cout << " → 决策: " << last_action_ << " " << symbol << " (价格 >= " << sell_threshold_ << ")" << std::endl;} else {last_action_ = "持有";std::cout << " → 决策: " << last_action_ << " " << symbol << " (观望中)" << std::endl;}}/*** @brief 获取观察者名称* * @return:投资者名称*/std::string getName() const override {return name_;}/*** @brief 获取上次操作* * @return:上次投资操作描述*/std::string getLastAction() const {return last_action_;}
};/*** @brief 机构观察者* * 代表机构投资者,具有更复杂的监控逻辑和报表功能。*/
class Institution : public StockObserver {
private:std::string institution_name_; ///< 机构名称std::unordered_map<std::string, double> price_history_; ///< 价格历史记录public:/*** @brief 构造函数* * @in:* - name: 机构名称*/explicit Institution(const std::string& name) : institution_name_(name) {}/*** @brief 更新方法实现* * 记录价格历史并生成分析报告。* * @in:* - subject: 发生价格变化的股票主题*/void update(const StockSubject* subject) override {const std::string& symbol = subject->getSymbol();double current_price = subject->getPrice();// 记录价格历史auto it = price_history_.find(symbol);if (it != price_history_.end()) {double old_price = it->second;double change = ((current_price - old_price) / old_price) * 100;std::cout << "[" << institution_name_ << "] 分析报告 - " << symbol << ": " << old_price << " → " << current_price<< " (" << (change >= 0 ? "+" : "") << change << "%)" << std::endl;}price_history_[symbol] = current_price;}/*** @brief 获取观察者名称* * @return:机构名称*/std::string getName() const override {return institution_name_;}/*** @brief 生成详细报告* * @return:包含所有监控股票的分析报告*/std::string generateReport() const {std::string report = institution_name_ + " 监控报告:\n";for (const auto& entry : price_history_) {report += " " + entry.first + ": " + std::to_string(entry.second) + "\n";}return report;}
};/*** @brief 主函数 - 股票监控系统演示* * 演示观察者模式在金融监控系统中的应用,展示动态订阅和自动通知机制。* * @return:程序退出码*/
int main() {std::cout << "=== 股票价格监控系统演示 ===" << std::endl;// 创建股票主题auto apple_stock = std::make_shared<ConcreteStock>("AAPL", 150.0);auto google_stock = std::make_shared<ConcreteStock>("GOOGL", 2800.0);// 创建投资者观察者auto investor1 = std::make_shared<Investor>("张投资者", 145.0, 160.0);auto investor2 = std::make_shared<Investor>("李投资者", 140.0, 155.0);auto institution = std::make_shared<Institution>("量子基金");std::cout << "\n--- 初始订阅阶段 ---" << std::endl;// 投资者订阅股票apple_stock->attach(investor1);apple_stock->attach(investor2);apple_stock->attach(institution);google_stock->attach(investor1);google_stock->attach(institution);std::cout << "\nAAPL 观察者数量: " << apple_stock->getObserverCount() << std::endl;std::cout << "GOOGL 观察者数量: " << google_stock->getObserverCount() << std::endl;std::cout << "\n--- 价格变化阶段 ---" << std::endl;// 模拟价格变化apple_stock->setPrice(148.5); // 小幅下跌apple_stock->setPrice(142.0); // 大幅下跌apple_stock->setPrice(158.0); // 大幅上涨google_stock->setPrice(2750.0); // 小幅下跌google_stock->setPrice(2850.0); // 上涨std::cout << "\n--- 动态订阅调整 ---" << std::endl;// 动态取消订阅apple_stock->detach(investor2);std::cout << "AAPL 观察者数量: " << apple_stock->getObserverCount() << std::endl;// 继续价格变化apple_stock->setPrice(162.0);std::cout << "\n--- 机构报告生成 ---" << std::endl;std::cout << institution->generateReport();std::cout << "\n--- 生命周期安全测试 ---" << std::endl;{// 创建临时观察者auto temp_investor = std::make_shared<Investor>("临时投资者", 150.0, 165.0);apple_stock->attach(temp_investor);std::cout << "临时观察者订阅后,AAPL 观察者数量: " << apple_stock->getObserverCount() << std::endl;// temp_investor 离开作用域自动销毁}// 即使临时观察者已销毁,这里也不会崩溃apple_stock->setPrice(155.0);std::cout << "临时观察者销毁后,AAPL 观察者数量: " << apple_stock->getObserverCount() << std::endl;std::cout << "\n=== 演示完成 ===" << std::endl;return 0;
}
Mermaid时序图:
3.2 案例二:游戏引擎事件系统
游戏引擎中广泛使用观察者模式来处理各种游戏事件。
#include <iostream>
#include <memory>
#include <vector>
#include <string>
#include <unordered_map>
#include <algorithm>// 游戏事件类型枚举
enum class EventType {PLAYER_SPAWN,PLAYER_DEATH,ITEM_PICKUP,LEVEL_COMPLETE,ACHIEVEMENT_UNLOCKED
};/*** @brief 游戏事件数据基类* * 所有具体游戏事件数据的基类,提供基本的事件信息。*/
struct GameEvent {EventType type; ///< 事件类型double timestamp; ///< 事件时间戳virtual ~GameEvent() = default;/*** @brief 获取事件描述* * @return:事件的文字描述*/virtual std::string getDescription() const = 0;
};/*** @brief 玩家生成事件*/
struct PlayerSpawnEvent : public GameEvent {std::string player_name; ///< 玩家名称int spawn_point_id; ///< 生成点IDPlayerSpawnEvent(const std::string& name, int spawn_id) : player_name(name), spawn_point_id(spawn_id) {type = EventType::PLAYER_SPAWN;}std::string getDescription() const override {return "玩家 " + player_name + " 在生成点 " + std::to_string(spawn_point_id) + " 生成";}
};/*** @brief 玩家死亡事件*/
struct PlayerDeathEvent : public GameEvent {std::string player_name; ///< 玩家名称std::string killer_name; ///< 击杀者名称std::string cause; ///< 死亡原因PlayerDeathEvent(const std::string& player, const std::string& killer, const std::string& death_cause): player_name(player), killer_name(killer), cause(death_cause) {type = EventType::PLAYER_DEATH;}std::string getDescription() const override {return "玩家 " + player_name + " 被 " + killer_name + " 击杀 (" + cause + ")";}
};/*** @brief 成就解锁事件*/
struct AchievementEvent : public GameEvent {std::string achievement_id; ///< 成就IDstd::string achievement_name; ///< 成就名称int points; ///< 成就点数AchievementEvent(const std::string& id, const std::string& name, int pts): achievement_id(id), achievement_name(name), points(pts) {type = EventType::ACHIEVEMENT_UNLOCKED;}std::string getDescription() const override {return "解锁成就: " + achievement_name + " (" + std::to_string(points) + "点)";}
};// 前向声明
class EventManager;/*** @brief 游戏事件观察者接口* * 游戏系统中各种组件通过实现此接口来监听感兴趣的游戏事件。*/
class GameEventListener {
public:virtual ~GameEventListener() = default;/*** @brief 事件处理接口* * @in:* - event: 发生的事件对象* - manager: 事件管理器,用于触发新事件*/virtual void onEvent(const std::shared_ptr<GameEvent>& event, EventManager* manager) = 0;/*** @brief 获取监听器名称* * @return:监听器标识名称*/virtual std::string getName() const = 0;/*** @brief 获取感兴趣的事件类型* * @return:该监听器关心的事件类型列表*/virtual std::vector<EventType> getInterestedEvents() const = 0;
};/*** @brief 事件管理器* * 游戏事件系统的核心,负责事件的发布和观察者管理。*/
class EventManager {
private:/*** @brief 事件类型到观察者列表的映射*/std::unordered_map<EventType, std::vector<std::weak_ptr<GameEventListener>>> event_listeners_;public:/*** @brief 注册事件监听器* * 将监听器注册到其感兴趣的所有事件类型上。* * @in:* - listener: 要注册的事件监听器*/void registerListener(const std::shared_ptr<GameEventListener>& listener) {for (EventType event_type : listener->getInterestedEvents()) {event_listeners_[event_type].emplace_back(listener);std::cout << "注册监听器: " << listener->getName() << " -> 事件类型: " << static_cast<int>(event_type) << std::endl;}}/*** @brief 取消注册事件监听器* * 从所有事件类型中移除指定的监听器。* * @in:* - listener: 要取消注册的事件监听器*/void unregisterListener(const std::shared_ptr<GameEventListener>& listener) {for (auto& pair : event_listeners_) {auto& listeners = pair.second;listeners.erase(std::remove_if(listeners.begin(), listeners.end(),[&listener](const std::weak_ptr<GameEventListener>& wp) {return wp.expired() || wp.lock() == listener;}),listeners.end());}std::cout << "取消注册监听器: " << listener->getName() << std::endl;}/*** @brief 发布事件* * 将事件发送给所有注册的对应类型监听器。* * @in:* - event: 要发布的事件对象*/void publishEvent(const std::shared_ptr<GameEvent>& event) {std::cout << "\n[事件发布] " << event->getDescription() << std::endl;auto it = event_listeners_.find(event->type);if (it != event_listeners_.end()) {// 清理失效的监听器auto& listeners = it->second;listeners.erase(std::remove_if(listeners.begin(), listeners.end(),[](const std::weak_ptr<GameEventListener>& wp) {return wp.expired();}),listeners.end());// 通知所有有效监听器for (auto& weak_listener : listeners) {if (auto listener = weak_listener.lock()) {listener->onEvent(event, this);}}}}/*** @brief 获取监听器统计信息* * @return:各事件类型的监听器数量统计*/std::string getStats() const {std::string stats = "事件监听器统计:\n";for (const auto& pair : event_listeners_) {int count = std::count_if(pair.second.begin(), pair.second.end(),[](const std::weak_ptr<GameEventListener>& wp) {return !wp.expired();});stats += " 事件类型 " + std::to_string(static_cast<int>(pair.first)) + ": " + std::to_string(count) + " 个监听器\n";}return stats;}
};/*** @brief 成就系统* * 监听游戏事件并解锁相应的成就。*/
class AchievementSystem : public GameEventListener {
private:std::unordered_map<std::string, bool> unlocked_achievements_; ///< 已解锁成就记录int total_points_; ///< 总成就点数public:AchievementSystem() : total_points_(0) {// 初始化成就列表unlocked_achievements_ = {{"first_blood", false},{"veteran", false},{"collector", false}};}void onEvent(const std::shared_ptr<GameEvent>& event, EventManager* manager) override {switch (event->type) {case EventType::PLAYER_DEATH: {// 检查是否是第一次击杀if (!unlocked_achievements_["first_blood"]) {unlocked_achievements_["first_blood"] = true;total_points_ += 10;// 发布成就解锁事件auto achievement_event = std::make_shared<AchievementEvent>("first_blood", "第一滴血", 10);manager->publishEvent(achievement_event);}break;}case EventType::PLAYER_SPAWN: {auto spawn_event = std::dynamic_pointer_cast<PlayerSpawnEvent>(event);if (spawn_event && spawn_event->spawn_point_id > 5 && !unlocked_achievements_["veteran"]) {unlocked_achievements_["veteran"] = true;total_points_ += 20;auto achievement_event = std::make_shared<AchievementEvent>("veteran", "战场老兵", 20);manager->publishEvent(achievement_event);}break;}case EventType::ITEM_PICKUP: {// 简单的物品收集成就逻辑if (!unlocked_achievements_["collector"]) {unlocked_achievements_["collector"] = true;total_points_ += 15;auto achievement_event = std::make_shared<AchievementEvent>("collector", "收藏家", 15);manager->publishEvent(achievement_event);}break;}default:break;}}std::string getName() const override {return "成就系统";}std::vector<EventType> getInterestedEvents() const override {return {EventType::PLAYER_DEATH,EventType::PLAYER_SPAWN,EventType::ITEM_PICKUP};}/*** @brief 获取成就系统状态* * @return:成就解锁状态和点数统计*/std::string getStatus() const {std::string status = "成就系统状态:\n";status += " 总点数: " + std::to_string(total_points_) + "\n";status += " 已解锁成就:\n";for (const auto& achievement : unlocked_achievements_) {status += " " + achievement.first + ": " + (achievement.second ? "已解锁" : "未解锁") + "\n";}return status;}
};/*** @brief 统计系统* * 收集游戏统计数据,如击杀数、死亡数等。*/
class StatisticsSystem : public GameEventListener {
private:int total_kills_; ///< 总击杀数int total_deaths_; ///< 总死亡数int total_spawns_; ///< 总生成次数public:StatisticsSystem() : total_kills_(0), total_deaths_(0), total_spawns_(0) {}void onEvent(const std::shared_ptr<GameEvent>& event, EventManager* manager) override {switch (event->type) {case EventType::PLAYER_DEATH:total_deaths_++;break;case EventType::PLAYER_SPAWN:total_spawns_++;break;default:break;}}std::string getName() const override {return "统计系统";}std::vector<EventType> getInterestedEvents() const override {return {EventType::PLAYER_DEATH,EventType::PLAYER_SPAWN};}/*** @brief 获取统计报告* * @return:游戏统计数据报告*/std::string getReport() const {return "游戏统计:\n 生成次数: " + std::to_string(total_spawns_) +"\n 死亡次数: " + std::to_string(total_deaths_) +"\n K/D比率: " + (total_deaths_ > 0 ? std::to_string(static_cast<double>(total_kills_) / total_deaths_) : "N/A");}
};/*** @brief 用户界面系统* * 在游戏UI中显示事件通知。*/
class UISystem : public GameEventListener {
public:void onEvent(const std::shared_ptr<GameEvent>& event, EventManager* manager) override {std::cout << "[UI通知] " << event->getDescription() << std::endl;}std::string getName() const override {return "UI系统";}std::vector<EventType> getInterestedEvents() const override {// UI系统关心所有事件类型return {EventType::PLAYER_SPAWN,EventType::PLAYER_DEATH,EventType::ITEM_PICKUP,EventType::LEVEL_COMPLETE,EventType::ACHIEVEMENT_UNLOCKED};}
};/*** @brief 主函数 - 游戏事件系统演示* * 演示观察者模式在游戏事件系统中的应用,展示多类型事件的处理机制。* * @return:程序退出码*/
int main() {std::cout << "=== 游戏引擎事件系统演示 ===" << std::endl;// 创建事件管理器EventManager event_manager;// 创建各种游戏系统auto achievement_system = std::make_shared<AchievementSystem>();auto stats_system = std::make_shared<StatisticsSystem>();auto ui_system = std::make_shared<UISystem>();// 注册系统到事件管理器std::cout << "\n--- 系统注册阶段 ---" << std::endl;event_manager.registerListener(achievement_system);event_manager.registerListener(stats_system);event_manager.registerListener(ui_system);std::cout << event_manager.getStats();std::cout << "\n--- 游戏事件模拟 ---" << std::endl;// 模拟游戏事件序列auto spawn_event1 = std::make_shared<PlayerSpawnEvent>("玩家1", 1);event_manager.publishEvent(spawn_event1);auto spawn_event2 = std::make_shared<PlayerSpawnEvent>("玩家2", 6); // 高级生成点event_manager.publishEvent(spawn_event2);auto death_event1 = std::make_shared<PlayerDeathEvent>("玩家1", "玩家2", "爆头");event_manager.publishEvent(death_event1);// 模拟物品拾取事件auto pickup_event = std::make_shared<GameEvent>();pickup_event->type = EventType::ITEM_PICKUP;event_manager.publishEvent(pickup_event);std::cout << "\n--- 系统状态报告 ---" << std::endl;std::cout << achievement_system->getStatus() << std::endl;std::cout << stats_system->getReport() << std::endl;std::cout << "\n--- 动态系统管理 ---" << std::endl;// 动态移除UI系统event_manager.unregisterListener(ui_system);// 后续事件不再通知UI系统auto death_event2 = std::make_shared<PlayerDeathEvent>("玩家2", "玩家1", "近战");event_manager.publishEvent(death_event2);std::cout << event_manager.getStats();std::cout << "\n=== 演示完成 ===" << std::endl;return 0;
}
Mermaid流程图:
3.3 案例三:配置管理系统
企业级配置管理系统使用观察者模式实现配置热更新。
#include <iostream>
#include <memory>
#include <vector>
#include <string>
#include <unordered_map>
#include <functional>
#include <sstream>/*** @brief 配置变更观察者接口* * 组件通过实现此接口来监听配置变化并自动更新自身状态。*/
class ConfigChangeListener {
public:virtual ~ConfigChangeListener() = default;/*** @brief 配置变更回调* * 当监听的配置项发生变化时调用此方法。* * @in:* - key: 发生变化的配置键* - old_value: 旧的配置值* - new_value: 新的配置值*/virtual void onConfigChanged(const std::string& key, const std::string& old_value, const std::string& new_value) = 0;/*** @brief 获取监听器名称* * @return:监听器标识名称*/virtual std::string getName() const = 0;/*** @brief 获取关心的配置键* * @return:该监听器关心的配置键列表*/virtual std::vector<std::string> getWatchedKeys() const = 0;
};/*** @brief 配置管理器* * 管理应用程序的配置数据,支持配置热更新和观察者通知。*/
class ConfigManager {
private:std::unordered_map<std::string, std::string> config_data_; ///< 配置数据存储/*** @brief 配置键到观察者列表的映射*/std::unordered_map<std::string, std::vector<std::weak_ptr<ConfigChangeListener>>> key_listeners_;/*** @brief 全局观察者列表(监听所有配置变化)*/std::vector<std::weak_ptr<ConfigChangeListener>> global_listeners_;public:/*** @brief 设置配置值* * 设置指定配置键的值,如果值发生变化则通知所有观察者。* * @in:* - key: 配置键* - value: 配置值*/void setConfig(const std::string& key, const std::string& value) {std::string old_value = getConfig(key, "");if (old_value != value) {config_data_[key] = value;std::cout << "配置更新: " << key << " = " << value << " (原值: " << (old_value.empty() ? "空" : old_value) << ")" << std::endl;// 通知特定键的观察者notifyKeyListeners(key, old_value, value);// 通知全局观察者notifyGlobalListeners(key, old_value, value);}}/*** @brief 获取配置值* * @in:* - key: 配置键* - default_value: 如果键不存在时返回的默认值* * @return:配置值或默认值*/std::string getConfig(const std::string& key, const std::string& default_value = "") const {auto it = config_data_.find(key);return it != config_data_.end() ? it->second : default_value;}/*** @brief 注册键监听器* * 注册观察者监听特定配置键的变化。* * @in:* - key: 要监听的配置键* - listener: 配置变更观察者*/void registerKeyListener(const std::string& key, const std::shared_ptr<ConfigChangeListener>& listener) {key_listeners_[key].emplace_back(listener);std::cout << "注册键监听: " << listener->getName() << " -> " << key << std::endl;}/*** @brief 注册全局监听器* * 注册观察者监听所有配置键的变化。* * @in:* - listener: 配置变更观察者*/void registerGlobalListener(const std::shared_ptr<ConfigChangeListener>& listener) {global_listeners_.emplace_back(listener);std::cout << "注册全局监听: " << listener->getName() << std::endl;}/*** @brief 批量注册监听器* * 根据观察者关心的键列表自动注册监听。* * @in:* - listener: 配置变更观察者*/void registerListener(const std::shared_ptr<ConfigChangeListener>& listener) {for (const auto& key : listener->getWatchedKeys()) {registerKeyListener(key, listener);}}/*** @brief 获取配置快照* * @return:当前所有配置的字符串表示*/std::string getConfigSnapshot() const {std::stringstream ss;ss << "当前配置快照:\n";for (const auto& entry : config_data_) {ss << " " << entry.first << " = " << entry.second << "\n";}return ss.str();}private:/*** @brief 通知键监听器* * 通知监听特定配置键的观察者。* * @in:* - key: 发生变化的配置键* - old_value: 旧值* - new_value: 新值*/void notifyKeyListeners(const std::string& key, const std::string& old_value, const std::string& new_value) {auto it = key_listeners_.find(key);if (it != key_listeners_.end()) {auto& listeners = it->second;// 清理失效的监听器listeners.erase(std::remove_if(listeners.begin(), listeners.end(),[](const std::weak_ptr<ConfigChangeListener>& wp) {return wp.expired();}),listeners.end());// 通知有效监听器for (auto& weak_listener : listeners) {if (auto listener = weak_listener.lock()) {listener->onConfigChanged(key, old_value, new_value);}}}}/*** @brief 通知全局监听器* * 通知监听所有配置变化的观察者。* * @in:* - key: 发生变化的配置键* - old_value: 旧值* - new_value: 新值*/void notifyGlobalListeners(const std::string& key, const std::string& old_value, const std::string& new_value) {// 清理失效的监听器global_listeners_.erase(std::remove_if(global_listeners_.begin(), global_listeners_.end(),[](const std::weak_ptr<ConfigChangeListener>& wp) {return wp.expired();}),global_listeners_.end());// 通知有效监听器for (auto& weak_listener : global_listeners_) {if (auto listener = weak_listener.lock()) {listener->onConfigChanged(key, old_value, new_value);}}}
};/*** @brief 数据库连接池* * 根据数据库配置变化动态调整连接池参数。*/
class DatabaseConnectionPool : public ConfigChangeListener {
private:std::string pool_name_; ///< 连接池名称int max_connections_; ///< 最大连接数int timeout_seconds_; ///< 超时时间public:explicit DatabaseConnectionPool(const std::string& name) : pool_name_(name), max_connections_(10), timeout_seconds_(30) {}void onConfigChanged(const std::string& key, const std::string& old_value, const std::string& new_value) override {if (key == "db.max_connections") {int new_max = std::stoi(new_value);if (new_max != max_connections_) {std::cout << "[" << pool_name_ << "] 更新最大连接数: " << max_connections_ << " -> " << new_max << std::endl;max_connections_ = new_max;applyPoolConfig();}} else if (key == "db.timeout") {int new_timeout = std::stoi(new_value);if (new_timeout != timeout_seconds_) {std::cout << "[" << pool_name_ << "] 更新超时时间: " << timeout_seconds_ << " -> " << new_timeout << "秒" << std::endl;timeout_seconds_ = new_timeout;applyPoolConfig();}}}std::string getName() const override {return "数据库连接池[" + pool_name_ + "]";}std::vector<std::string> getWatchedKeys() const override {return {"db.max_connections","db.timeout"};}/*** @brief 应用连接池配置* * 根据当前配置参数调整连接池状态。*/void applyPoolConfig() {std::cout << "[" << pool_name_ << "] 应用配置: " << max_connections_ << "连接, " << timeout_seconds_ << "秒超时" << std::endl;}/*** @brief 获取连接池状态* * @return:连接池当前状态描述*/std::string getStatus() const {return "连接池状态: " + std::to_string(max_connections_) + "连接, " + std::to_string(timeout_seconds_) + "秒超时";}
};/*** @brief 日志系统* * 根据日志配置变化动态调整日志级别和输出目标。*/
class LogSystem : public ConfigChangeListener {
private:std::string log_level_; ///< 当前日志级别std::string output_target_; ///< 输出目标public:LogSystem() : log_level_("INFO"), output_target_("console") {}void onConfigChanged(const std::string& key, const std::string& old_value, const std::string& new_value) override {if (key == "log.level") {std::cout << "[日志系统] 更新日志级别: " << log_level_ << " -> " << new_value << std::endl;log_level_ = new_value;applyLogConfig();} else if (key == "log.output") {std::cout << "[日志系统] 更新输出目标: " << output_target_ << " -> " << new_value << std::endl;output_target_ = new_value;applyLogConfig();}}std::string getName() const override {return "日志系统";}std::vector<std::string> getWatchedKeys() const override {return {"log.level","log.output"};}/*** @brief 应用日志配置* * 根据当前配置参数调整日志系统行为。*/void applyLogConfig() {std::cout << "[日志系统] 应用配置: 级别=" << log_level_ << ", 输出=" << output_target_ << std::endl;}/*** @brief 记录日志* * @in:* - level: 日志级别* - message: 日志消息*/void log(const std::string& level, const std::string& message) {// 简单的级别过滤if (shouldLog(level)) {std::cout << "[" << level << "] " << message << std::endl;}}private:/*** @brief 检查是否应该记录指定级别的日志* * @in:* - level: 要检查的日志级别* * @return:如果应该记录返回true*/bool shouldLog(const std::string& level) const {static const std::unordered_map<std::string, int> level_priority = {{"DEBUG", 0},{"INFO", 1},{"WARN", 2},{"ERROR", 3},{"FATAL", 4}};auto current_it = level_priority.find(log_level_);auto check_it = level_priority.find(level);if (current_it != level_priority.end() && check_it != level_priority.end()) {return check_it->second >= current_it->second;}return false;}
};/*** @brief 配置审计系统* * 记录所有配置变更的审计日志,用于安全监控。*/
class ConfigAuditSystem : public ConfigChangeListener {
public:void onConfigChanged(const std::string& key, const std::string& old_value, const std::string& new_value) override {std::cout << "[审计系统] 配置变更审计: " << key << " = " << old_value << " -> " << new_value << std::endl;// 在实际系统中,这里会将审计记录写入数据库或文件writeAuditLog(key, old_value, new_value);}std::string getName() const override {return "配置审计系统";}std::vector<std::string> getWatchedKeys() const override {// 审计系统关心所有配置键return {}; // 空列表表示监听所有键(通过全局监听注册)}private:/*** @brief 写入审计日志* * @in:* - key: 配置键* - old_value: 旧值* - new_value: 新值*/void writeAuditLog(const std::string& key, const std::string& old_value, const std::string& new_value) {// 模拟写入审计日志// 实际实现可能写入数据库、文件或发送到审计服务static int audit_id = 0;audit_id++;// 这里只是演示,实际系统会有更完整的审计记录}
};/*** @brief 主函数 - 配置管理系统演示* * 演示观察者模式在配置管理系统中的应用,展示配置热更新和组件自动调整。* * @return:程序退出码*/
int main() {std::cout << "=== 配置管理系统演示 ===" << std::endl;// 创建配置管理器ConfigManager config_manager;// 创建各种系统组件auto db_pool = std::make_shared<DatabaseConnectionPool>("主数据库");auto log_system = std::make_shared<LogSystem>();auto audit_system = std::make_shared<ConfigAuditSystem>();std::cout << "\n--- 组件注册阶段 ---" << std::endl;// 注册组件到配置管理器config_manager.registerListener(db_pool);config_manager.registerListener(log_system);config_manager.registerGlobalListener(audit_system); // 审计系统监听所有变更std::cout << "\n--- 初始配置设置 ---" << std::endl;// 设置初始配置config_manager.setConfig("db.max_connections", "20");config_manager.setConfig("db.timeout", "60");config_manager.setConfig("log.level", "INFO");config_manager.setConfig("log.output", "file");std::cout << config_manager.getConfigSnapshot();std::cout << "\n--- 配置热更新演示 ---" << std::endl;// 模拟运行时配置更新config_manager.setConfig("db.max_connections", "50"); // 增加连接数config_manager.setConfig("log.level", "DEBUG"); // 调整日志级别config_manager.setConfig("app.cache_size", "1024"); // 新增配置(审计系统会记录)std::cout << "\n--- 组件状态检查 ---" << std::endl;// 检查组件状态log_system->log("DEBUG", "这是一条调试消息");log_system->log("INFO", "这是一条信息消息");std::cout << "\n--- 批量配置更新 ---" << std::endl;// 模拟从外部配置文件重新加载配置config_manager.setConfig("db.max_connections", "30");config_manager.setConfig("db.timeout", "45");config_manager.setConfig("log.level", "WARN");config_manager.setConfig("log.output", "console");std::cout << "\n最终配置状态:" << std::endl;std::cout << config_manager.getConfigSnapshot();std::cout << "\n=== 演示完成 ===" << std::endl;return 0;
}
4. Makefile范例
# 编译器配置
CXX := g++
CXX_STD := c++17# 编译选项
CXXFLAGS := -std=$(CXX_STD) -Wall -Wextra -Wpedantic -O2
DEBUG_FLAGS := -g -DDEBUG -O0
RELEASE_FLAGS := -DNDEBUG -O3# 目标文件
TARGETS := stock_system game_events config_system# 默认构建所有目标
all: $(TARGETS)# 股票监控系统
stock_system: stock_system.cpp$(CXX) $(CXXFLAGS) $(RELEASE_FLAGS) -o $@ $<# 游戏事件系统
game_events: game_events.cpp$(CXX) $(CXXFLAGS) $(RELEASE_FLAGS) -o $@ $<# 配置管理系统
config_system: config_system.cpp$(CXX) $(CXXFLAGS) $(RELEASE_FLAGS) -o $@ $<# 调试版本
debug: CXXFLAGS += $(DEBUG_FLAGS)
debug: $(TARGETS)# 清理构建文件
clean:rm -f $(TARGETS) *.o# 安装依赖(Ubuntu/Debian)
install-deps:sudo apt-get updatesudo apt-get install -y g++ build-essential# 运行所有测试
test: all@echo "运行股票系统测试..."@./stock_system@echo@echo "运行游戏事件系统测试..."@./game_events@echo@echo "运行配置系统测试..."@./config_system.PHONY: all clean debug install-deps test
5. 操作说明
5.1 编译方法
环境要求:
- GCC 7.0+ 或 Clang 5.0+
- C++17 支持
- Linux/Unix 系统(Windows需使用MinGW或WSL)
编译命令:
# 安装编译依赖(Ubuntu/Debian)
make install-deps# 编译所有目标
make# 编译调试版本
make debug# 编译并运行所有测试
make test
5.2 运行方式
股票监控系统:
./stock_system
输出示例:
=== 股票价格监控系统演示 ===--- 初始订阅阶段 ---
观察者 张投资者 开始监控股票 AAPL
观察者 李投资者 开始监控股票 AAPL
观察者 量子基金 开始监控股票 AAPL
观察者 张投资者 开始监控股票 GOOGL
观察者 量子基金 开始监控股票 GOOGL--- 价格变化阶段 ---
股票 AAPL 价格变化: 150 -> 148.5
[张投资者] 收到通知: AAPL 当前价格 = 148.5→ 决策: 持有 AAPL (观望中)
[李投资者] 收到通知: AAPL 当前价格 = 148.5→ 决策: 持有 AAPL (观望中)
[量子基金] 分析报告 - AAPL: 150 → 148.5 (-1%)
游戏事件系统:
./game_events
输出示例:
=== 游戏引擎事件系统演示 ===--- 系统注册阶段 ---
注册监听器: 成就系统 -> 事件类型: 2
注册监听器: 成就系统 -> 事件类型: 0
注册监听器: 成就系统 -> 事件类型: 1
注册监听器: 统计系统 -> 事件类型: 2
注册监听器: 统计系统 -> 事件类型: 0
注册监听器: UI系统 -> 事件类型: 0
...--- 游戏事件模拟 ---
[事件发布] 玩家 玩家1 在生成点 1 生成
[UI通知] 玩家 玩家1 在生成点 1 生成
[事件发布] 玩家 玩家2 在生成点 6 生成
[UI通知] 玩家 玩家2 在生成点 6 生成
[成就系统] 分析报告 - : 0 → 0 (+0%)
配置管理系统:
./config_system
输出示例:
=== 配置管理系统演示 ===--- 组件注册阶段 ---
注册键监听: 数据库连接池[主数据库] -> db.max_connections
注册键监听: 数据库连接池[主数据库] -> db.timeout
注册键监听: 日志系统 -> log.level
注册键监听: 日志系统 -> log.output
注册全局监听: 配置审计系统--- 初始配置设置 ---
配置更新: db.max_connections = 20 (原值: 空)
[审计系统] 配置变更审计: db.max_connections = -> 20
配置更新: db.timeout = 60 (原值: 空)
[审计系统] 配置变更审计: db.timeout = -> 60
...
5.3 结果解读
正常输出特征:
- 股票系统:投资者根据价格阈值自动做出买卖决策
- 游戏系统:各子系统协同处理游戏事件,成就自动解锁
- 配置系统:配置变更时各组件自动调整行为
设计模式优势体现:
- 解耦性:主题和观察者相互独立,可以独立变化
- 扩展性:新增观察者无需修改主题代码
- 动态性:运行时可以动态添加、删除观察者关系
6. 深入解析:生命周期管理的最佳实践
6.1 weak_ptr的安全使用模式
观察者模式中最重要的技术点就是使用std::weak_ptr
避免循环引用和悬空指针:
class SafeSubject {
private:std::vector<std::weak_ptr<Observer>> observers_;public:void attach(const std::shared_ptr<Observer>& observer) {// 使用weak_ptr避免循环引用observers_.push_back(observer);}void notify() {// 先清理已失效的观察者observers_.erase(std::remove_if(observers_.begin(), observers_.end(),[](const auto& wp) { return wp.expired(); }),observers_.end());// 再通知有效的观察者for (auto& weak_obs : observers_) {if (auto observer = weak_obs.lock()) {observer->update();}}}
};
6.2 观察者模式的变体与扩展
1. 推模型 vs 拉模型
// 推模型:主题将详细数据推送给观察者
virtual void update(const EventData& data) = 0;// 拉模型:观察者从主题拉取需要的数据
virtual void update(Subject* subject) = 0;
2. 事件过滤机制
class SelectiveObserver : public Observer {
private:std::function<bool(const EventData&)> filter_;public:void update(const EventData& data) override {if (filter_(data)) {// 只处理符合条件的事件processEvent(data);}}
};
3. 异步通知机制
class AsyncSubject : public Subject {
private:std::thread notification_thread_;public:void asyncNotify() {notification_thread_ = std::thread([this]() {std::this_thread::sleep_for(std::chrono::milliseconds(10));notify(); // 在后台线程通知});}
};
7. 总结
观察者模式是构建松耦合、可扩展系统的利器。通过本文的三个完整案例,我们深入掌握了:
核心价值:
- 实现主题与观察者的解耦,提高代码的可维护性
- 支持广播通信,简化一对多的依赖关系管理
- 提供动态订阅机制,增强系统灵活性
关键技术:
- 使用
std::weak_ptr
安全管理观察者生命周期 - 通过模板和继承支持多种事件类型
- 实现高效的通知和清理机制
适用场景:
- 事件驱动架构:GUI系统、游戏引擎
- 数据监控系统:股票行情、系统监控
- 配置管理系统:热更新、动态调整
- 分布式系统:消息队列、发布订阅
观察者模式虽然简单,但却是构建现代软件系统的基础模式。掌握它,就掌握了构建响应式、可扩展系统的关键技能。