C++中的事件总线与观察者模式:从理论到现代实现
在现代C++开发中,越来越需要一种灵活的组件间消息传递机制,而这些组件无需直接了解彼此。观察者模式及其演进——事件总线——解决了这个问题,使得构建松耦合、可扩展的系统成为可能。在本文中,我们将探讨如何使用标准库(C++17/20)在C++中实现事件总线,并研究实际示例。
观察者模式:经典基础
观察者模式定义了对象之间的一对多关系:当一个对象(主体)改变其状态时,所有依赖对象(观察者)都会自动收到通知。在C++中,经典实现如下:
#include <iostream>#include <vector>#include <memory>#include <algorithm>
// 观察者基类class IObserver {public: virtual ~IObserver() = default; virtual void update(const std::string& message) = 0;};
// 主体(发布者)class Subject {private: std::vector<std::weak_ptr<IObserver>> observers_;public: void attach(std::shared_ptr<IObserver> observer) { observers_.push_back(observer); }
void detach(std::shared_ptr<IObserver> observer) { observers_.erase( std::remove_if(observers_.begin(), observers_.end(), [&observer](const std::weak_ptr<IObserver>& wp) { auto sp = wp.lock(); return !sp || sp == observer; }), observers_.end() ); }
void notify(const std::string& message) { for (auto it = observers_.begin(); it != observers_.end(); ) { if (auto sp = it->lock()) { sp->update(message); ++it; } else { it = observers_.erase(it); // 移除"失效"引用 } } }};
// 具体观察者class ConcreteObserver : public IObserver {private: std::string name_;public: ConcreteObserver(const std::string& name) : name_(name) {} void update(const std::string& message) override { std::cout << "[" << name_ << "] 收到: " << message << std::endl; }};
int main() { auto subject = std::make_shared<Subject>(); auto obs1 = std::make_shared<ConcreteObserver>("观察者 1"); auto obs2 = std::make_shared<ConcreteObserver>("观察者 2");
subject->attach(obs1); subject->attach(obs2); subject->notify("事件发生了!");
subject->detach(obs1); subject->notify("仅针对第二个");
return 0;}这段代码可以工作,但它有局限性:所有观察者都必须继承IObserver,并且消息必须是字符串。在实际应用中,需要更灵活的系统。
事件总线:现代演进
事件总线是一个集中式总线,组件可以通过它发布事件并订阅事件。与经典观察者模式的关键区别:
- 类型化事件——每个事件都有自己的数据类型。
- 无严格层次结构——订阅者无需继承通用接口。
- 全局或局部总线——可以有一个适用于整个应用程序的事件总线,也可以有多个隔离的事件总线。
使用std::any在C++17/20中实现事件总线
使用std::any存储任意类型的数据,使用std::function作为处理函数。为了按类型区分事件,我们使用std::type_index。
#include <iostream>#include <any>#include <function