1. 观察者模式的核心价值与应用场景
在C++开发中,观察者模式(Observer Pattern)是我最常使用的设计模式之一。它特别适合处理对象间一对多的依赖关系——当一个对象状态改变时,所有依赖它的对象都能自动收到通知并更新。这种机制在GUI事件处理、游戏开发、数据监控等场景中尤为常见。
举个实际例子:假设我们正在开发一个股票交易系统。当某支股票价格变动时,需要实时通知多个显示面板(如K线图、成交量图表、预警系统等)。如果用传统轮询方式检查价格变化,不仅效率低下,还会造成资源浪费。而观察者模式能让价格数据源(被观察者)主动推送更新给所有订阅者(观察者),这正是它被称为"发布-订阅模式"的原因。
2. C++实现观察者模式的经典结构
2.1 基础类设计
我们先定义两个核心接口类:
// 观察者接口 class IObserver { public: virtual ~IObserver() = default; virtual void Update(const std::string& message) = 0; }; // 被观察者接口 class ISubject { public: virtual ~ISubject() = default; virtual void Attach(IObserver* observer) = 0; virtual void Detach(IObserver* observer) = 0; virtual void Notify() = 0; };2.2 具体实现示例
以天气预报系统为例,我们实现一个具体的被观察者:
class WeatherStation : public ISubject { private: std::list<IObserver*> observers_; std::string weather_; public: void Attach(IObserver* observer) override { observers_.push_back(observer); } void Detach(IObserver* observer) override { observers_.remove(observer); } void Notify() override { for (auto observer : observers_) { observer->Update(weather_); } } void SetWeather(const std::string& weather) { weather_ = weather; Notify(); // 天气变化时自动通知所有观察者 } };对应的观察者实现:
class PhoneDisplay : public IObserver { private: std::string weather_; public: void Update(const std::string& message) override { weather_ = message; std::cout << "手机端:当前天气已更新为 " << weather_ << std::endl; } };3. 现代C++的优化实现
3.1 使用智能指针管理生命周期
原始指针容易导致内存泄漏,我们可以用shared_ptr:
class ModernSubject { private: std::vector<std::shared_ptr<IObserver>> observers_; public: void Attach(std::shared_ptr<IObserver> observer) { observers_.push_back(observer); } // ...其他方法 };3.2 使用lambda简化回调
C++11后可以用function和lambda:
class EventBus { public: using Callback = std::function<void(const std::string&)>; void Subscribe(Callback cb) { callbacks_.push_back(cb); } void Publish(const std::string& msg) { for (auto& cb : callbacks_) { cb(msg); } } private: std::vector<Callback> callbacks_; };4. 多线程环境下的线程安全实现
4.1 基本的互斥锁保护
class ThreadSafeSubject : public ISubject { private: std::list<IObserver*> observers_; std::mutex mtx_; public: void Attach(IObserver* observer) override { std::lock_guard<std::mutex> lock(mtx_); observers_.push_back(observer); } // ...其他方法类似加锁 };4.2 避免死锁的注意事项
- 不要在Notify()方法内调用观察者的方法时持有锁
- 考虑使用std::recursive_mutex处理嵌套调用
- 对于高性能场景,可考虑无锁队列实现
5. 实际项目中的性能优化技巧
5.1 观察者优先级系统
struct ObserverRecord { IObserver* observer; int priority; // 数值越小优先级越高 }; void Notify() { // 按优先级排序后通知 std::sort(observers_.begin(), observers_.end(), [](auto& a, auto& b) { return a.priority < b.priority; }); // ...通知逻辑 }5.2 批量更新机制
对于高频更新场景,可以积累多次变化后批量通知:
void SetWeather(const std::string& weather) { weather_ = weather; dirty_ = true; // 标记需要更新 } // 由外部定时调用 void FlushUpdates() { if (dirty_) { Notify(); dirty_ = false; } }6. 常见问题与调试技巧
6.1 内存泄漏检测
使用weak_ptr打破循环引用:
class SafeObserver : public std::enable_shared_from_this<SafeObserver> { std::vector<std::weak_ptr<ISubject>> subjects_; void Subscribe(std::shared_ptr<ISubject> subject) { subjects_.emplace_back(subject); subject->Attach(shared_from_this()); } };6.2 调试观察者未触发
- 在Attach/Detach中添加日志输出
- 使用断点检查observers_容器内容
- 实现一个LoggingObserver来跟踪通知流
7. 与其他模式的结合应用
7.1 组合命令模式
class CommandObserver : public IObserver { public: void Update(const std::string&) override { command_->Execute(); } void SetCommand(std::unique_ptr<ICommand> cmd) { command_ = std::move(cmd); } private: std::unique_ptr<ICommand> command_; };7.2 与状态模式配合
当观察者需要根据被观察者状态改变自身行为时:
void Update(const std::string& state) override { if (state == "ALERT") { currentState_ = std::make_unique<AlertState>(); } else { currentState_ = std::make_unique<NormalState>(); } currentState_->Handle(); }在大型C++项目中,我通常会建立一个专门的观察者管理系统来集中管理所有订阅关系。这个系统可以提供:
- 订阅关系的可视化展示
- 通知性能统计
- 自动泄漏检测
- 跨线程通知队列
观察者模式看似简单,但在实际项目中要处理好线程安全、生命周期管理、性能优化等问题,需要开发者对C++特性有深入理解。特别是在高频交易系统或游戏引擎中,一个优化不当的观察者实现可能成为性能瓶颈。