观察者模式(Observer)深度解析:从理论到C++实战
1. 背景与核心概念
1.1 设计模式的起源与发展
观察者模式是软件工程中最经典、最常用的设计模式之一,它的历史可以追溯到面向对象编程的早期阶段。让我们回顾一下它的发展历程:
时期 | 主要贡献 | 应用场景 | 技术特点 |
---|---|---|---|
1970s | Smalltalk MVC框架 | GUI应用程序 | 模型-视图分离 |
1994 | GoF《设计模式》 | 事件处理系统 | 标准化模式描述 |
2000s | .NET事件委托 | 桌面应用开发 | 语言级支持 |
2010s | 响应式编程 | Web前端、移动端 | 数据流驱动 |
观察者模式的核心思想源于现实世界中的订阅-发布机制。就像报纸订阅一样:
- 出版社(主题)维护订阅者列表
- 读者(观察者)可以订阅或取消订阅
- 新报纸出版时,所有订阅者自动收到通知
1.2 模式定义与关键角色
观察者模式:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
让我们用Mermaid类图来理解各个角色的关系:
维护观察者列表 Subject +attach(Observer* observer) : void +detach(Observer* observer) : void +notify() : void ConcreteSubject -state : int +getState() : int +setState(int state) : void <<interface>> Observer +update(Subject* subject) : void ConcreteObserver -observerState : int +update(Subject* subject) : void
关键角色解析:
- Subject(主题):维护观察者列表,提供添加、删除和通知观察者的接口
- ConcreteSubject(具体主题):存储感兴趣的状态,状态改变时通知观察者
- Observer(观察者):为所有具体观察者定义更新接口
- ConcreteObserver(具体观察者):维护与主题状态一致的自身状态
1.3 MVC架构中的核心地位
观察者模式是MVC(Model-View-Controller)架构的核心机制:
通知更新 通知更新 通知更新 修改数据 Model 数据模型 View 视图1 View 视图2 View 视图3 Controller 控制器
在这种架构中:
- Model 充当主题角色,维护业务数据和状态
- View 充当观察者角色,监听Model的变化并更新显示
- Controller 处理用户输入,修改Model状态
2. 设计意图与考量
2.1 核心设计目标
观察者模式的设计主要围绕以下几个目标:
1. 解耦性
- 主题不需要知道观察者的具体类
- 观察者可以独立变化和复用
- 降低系统各部分之间的依赖
2. 动态关系
- 运行时可以动态添加、删除观察者
- 观察者关系可以随时改变
- 支持灵活的订阅机制
3. 广播通信
- 支持一对多的通信模式
- 主题变化自动通知所有观察者
- 避免手动维护更新逻辑
2.2 生命周期管理的挑战
观察者模式中最棘手的问题就是生命周期管理。考虑以下场景:
cpp
// 危险代码示例:悬空指针问题
Subject* subject = new Subject();
Observer* observer = new ConcreteObserver();
subject->attach(observer); // 主题持有观察者指针
// 之后某个时刻...
delete observer; // 观察者被销毁
// 但主题仍然持有已销毁对象的指针!
subject->notify(); // 灾难:访问已释放内存
解决方案对比:
方案 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
原始指针 | 简单直接,无额外开销 | 容易产生悬空指针 | 简单场景,生命周期明确 |
shared_ptr | 自动内存管理 | 循环引用导致内存泄漏 | 需要共享所有权的场景 |
weak_ptr | 避免循环引用,安全 | 使用稍复杂 | 观察者模式推荐方案 |
2.3 性能与扩展性权衡
通知性能考量:
- 观察者数量增加时,通知开销线性增长
- 可能需要支持按优先级通知
- 考虑异步通知机制避免阻塞
扩展性设计:
- 支持不同类型的观察者
- 允许观察者过滤感兴趣的事件
- 提供批量更新机制
3. 实例与应用场景
3.1 案例一:股票价格监控系统
这是一个典型的观察者模式应用,多个投资者监控同一支股票的价格变化。
cpp
#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时序图:
AppleStock 张投资者 李投资者 量子基金 初始状态:价格150.0 attach(张投资者) attach(李投资者) attach(量子基金) setPrice(148.5) update() update() update() 所有观察者收到价格下跌通知 setPrice(142.0) update() update() update() detach(李投资者) setPrice(158.0) update() update() 李投资者已取消订阅,不再接收通知 AppleStock 张投资者 李投资者 量子基金
3.2 案例二:游戏引擎事件系统
游戏引擎中广泛使用观察者模式来处理各种游戏事件。
cpp
#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; ///< 生成点ID
PlayerSpawnEvent(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; ///< 成就ID
std::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流程图:
是 否 否 是 游戏事件发生 EventManager.publishEvent 查找对应事件类型的监听器列表 清理已失效的监听器 遍历有效监听器 监听器处理事件? 调用onEvent方法 跳过此监听器 监听器可能发布新事件 所有监听器处理完成? 事件处理完成
3.3 案例三:配置管理系统
企业级配置管理系统使用观察者模式实现配置热更新。
cpp
#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范例
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 update
sudo 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)
编译命令:
bash
# 安装编译依赖(Ubuntu/Debian)
make install-deps
# 编译所有目标
make
# 编译调试版本
make debug
# 编译并运行所有测试
make test
5.2 运行方式
股票监控系统:
bash
./stock_system
输出示例:
=== 股票价格监控系统演示 ===
--- 初始订阅阶段 ---
观察者 张投资者 开始监控股票 AAPL
观察者 李投资者 开始监控股票 AAPL
观察者 量子基金 开始监控股票 AAPL
观察者 张投资者 开始监控股票 GOOGL
观察者 量子基金 开始监控股票 GOOGL
--- 价格变化阶段 ---
股票 AAPL 价格变化: 150 -> 148.5
[张投资者] 收到通知: AAPL 当前价格 = 148.5
→ 决策: 持有 AAPL (观望中)
[李投资者] 收到通知: AAPL 当前价格 = 148.5
→ 决策: 持有 AAPL (观望中)
[量子基金] 分析报告 - AAPL: 150 → 148.5 (-1%)
游戏事件系统:
bash
./game_events
输出示例:
=== 游戏引擎事件系统演示 ===
--- 系统注册阶段 ---
注册监听器: 成就系统 -> 事件类型: 2
注册监听器: 成就系统 -> 事件类型: 0
注册监听器: 成就系统 -> 事件类型: 1
注册监听器: 统计系统 -> 事件类型: 2
注册监听器: 统计系统 -> 事件类型: 0
注册监听器: UI系统 -> 事件类型: 0
...
--- 游戏事件模拟 ---
[事件发布] 玩家 玩家1 在生成点 1 生成
[UI通知] 玩家 玩家1 在生成点 1 生成
[事件发布] 玩家 玩家2 在生成点 6 生成
[UI通知] 玩家 玩家2 在生成点 6 生成
[成就系统] 分析报告 - : 0 → 0 (+0%)
配置管理系统:
bash
./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
避免循环引用和悬空指针:
cpp
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 拉模型
cpp
// 推模型:主题将详细数据推送给观察者
virtual void update(const EventData& data) = 0;
// 拉模型:观察者从主题拉取需要的数据
virtual void update(Subject* subject) = 0;
2. 事件过滤机制
cpp
class SelectiveObserver : public Observer {
private:
std::function<bool(const EventData&)> filter_;
public:
void update(const EventData& data) override {
if (filter_(data)) {
// 只处理符合条件的事件
processEvent(data);
}
}
};
3. 异步通知机制
cpp
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系统、游戏引擎
- 数据监控系统:股票行情、系统监控
- 配置管理系统:热更新、动态调整
- 分布式系统:消息队列、发布订阅
观察者模式虽然简单,但却是构建现代软件系统的基础模式。掌握它,就掌握了构建响应式、可扩展系统的关键技能。