C++14支持泛型容器的灵活观察者事件系统实现难题
问题:C++14观察者模式事件系统的容器接口泛化设计
我正在基于C++14实现一个支持观察者模式的事件系统,要求能灵活切换vector、map等不同容器管理观察者。当前系统包含Observer类、负责挂载/卸载观察者的Subject类,以及继承自Subject的EventSystem类,目标是让EventSystem可以自定义使用VectorContainer、MapContainer等容器类型。
核心问题是:希望泛化EventSystem中的容器类型,使其同时兼容单元素容器(如VectorContainer)和键值对容器(如MapContainer<Key, Value>),但现有IContainer接口仅支持单个模板参数,无法适配带双参数的MapContainer,难以设计出能同时处理单个元素与键值对的统一容器接口。
现有代码结构如下:
#include <functional> #include <unordered_map> #include <iostream> #include <type_traits> template<typename MessageType> class Observer { public: virtual ~Observer() = default; void Handle(const MessageType& message) { ApplyHandle(message); } private: virtual void ApplyHandle(const MessageType& message) = 0; }; template<typename MessageType, typename ObserverType, typename observerId> class Subject { public: virtual ~Subject() = default; virtual void Attach(ObserverType& observer, const observerId& observerID) = 0; virtual void Detach(const observerId& observerID) = 0; }; template<typename MessageType, typename ObserverType, typename observerId, typename ContainerType> class EventSystem : public Subject<MessageType, ObserverType, observerId> { public: ~EventSystem() = default; void Attach(const observerId& observerID, ObserverType& observer) override { mContainer.Add(observerID, observer); } void Detach(const observerId& observerID) override { mContainer.Remove(observerID); } private: ContainerType mContainer; }; template <typename T> class IContainer { public: virtual void Add(const T& item) = 0; // Adds item virtual void Remove(const T& item) = 0; // Remove the item if it exists in the container virtual ~IContainer() = default; }; template <typename T, typename ContainerType> class Container : public IContainer<T> { public: void Add(const T& item) override { container.Add(item); } void Remove(const T& item) override { container.Remove(item); } private: ContainerType container; }; template <typename T> class VectorContainer { public: void Add(const T& item) { vect.push_back(item); } void Remove(const T& item) { // Assuming T has a valid equality comparison auto it = std::remove(vect.begin(), vect.end(), item); vect.erase(it, vect.end()); } private: std::vector<T> vect; }; template <typename Key, typename Value> class MapContainer { public: void Add(const std::pair<const Key, Value>& keyValue) { container.insert(keyValue); } void Add(const Key& key, const Value& value) { container[key] = value; } void Remove(const std::pair<const Key, Value>& keyValue) { container.erase(keyValue.first); } private: std::map<Key, Value> container; };
解决方案1:直接适配容器方法签名
核心思路是放弃强制统一的单参数容器接口,让容器直接实现EventSystem所需的方法契约(Add(observerId, observer)和Remove(observerId)),通过适配不同容器的调用逻辑来兼容两类容器。
修改后的关键代码:
1. 适配VectorContainer,添加支持键值对的方法
template <typename T> class VectorContainer { public: // 保留原有单元素添加接口 void Add(const T& item) { vect.push_back(item); } // 适配EventSystem的Attach调用:将ID和观察者打包为pair存储 void Add(const auto& observerId, T& observer) { vect.push_back(std::make_pair(observerId, std::ref(observer))); } // 保留原有单元素移除接口 void Remove(const T& item) { auto it = std::remove(vect.begin(), vect.end(), item); vect.erase(it, vect.end()); } // 适配EventSystem的Detach调用:按ID查找并移除 void Remove(const auto& observerId) { auto it = std::remove_if(vect.begin(), vect.end(), [&observerId](const auto& pair) { return pair.first == observerId; }); vect.erase(it, vect.end()); } // 提供遍历接口供事件触发使用 auto begin() { return vect.begin(); } auto end() { return vect.end(); } private: std::vector<std::pair<decltype(std::declval<observerId>()), std::reference_wrapper<T>>> vect; };
2. 调整MapContainer,优化观察者存储方式
template <typename Key, typename Value> class MapContainer { public: void Add(const std::pair<const Key, Value>& keyValue) { container.insert(keyValue); } // 直接适配EventSystem的Attach调用 void Add(const Key& key, Value& value) { container[key] = std::ref(value); // 存储引用避免拷贝 } // 适配EventSystem的Detach调用 void Remove(const Key& key) { container.erase(key); } void Remove(const std::pair<const Key, Value>& keyValue) { container.erase(keyValue.first); } // 提供遍历接口 auto begin() { return container.begin(); } auto end() { return container.end(); } private: std::map<Key, std::reference_wrapper<Value>> container; };
3. 简化EventSystem,直接依赖容器契约
template<typename MessageType, typename ObserverType, typename observerId, typename ContainerType> class EventSystem : public Subject<MessageType, ObserverType, observerId> { public: ~EventSystem() = default; void Attach(const observerId& observerID, ObserverType& observer) override { mContainer.Add(observerID, observer); } void Detach(const observerId& observerID) override { mContainer.Remove(observerID); } // 事件触发实现 void Trigger(const MessageType& message) { for (auto& entry : mContainer) { // 统一处理vector的pair和map的entry auto& observer = std::is_same<decltype(entry), std::pair<observerId, std::reference_wrapper<ObserverType>>>::value ? entry.second : entry.second; observer.get().Handle(message); } } private: ContainerType mContainer; };
解决方案2:CRTP静态多态接口
如果需要保留接口抽象且避免虚函数开销,可以使用**奇异递归模板模式(CRTP)**实现静态多态,让不同容器继承统一的模板接口:
关键代码:
// CRTP基类,定义容器接口契约 template<typename Derived> class ContainerInterface { public: template<typename... Args> void Add(Args&&... args) { static_cast<Derived*>(this)->Add(std::forward<Args>(args)...); } template<typename... Args> void Remove(Args&&... args) { static_cast<Derived*>(this)->Remove(std::forward<Args>(args)...); } auto begin() { return static_cast<Derived*>(this)->begin(); } auto end() { return static_cast<Derived*>(this)->end(); } }; // VectorContainer继承CRTP接口 template <typename T> class VectorContainer : public ContainerInterface<VectorContainer<T>> { public: void Add(const T& item) { /* 原有实现 */ } void Add(const auto& id, T& observer) { /* 适配实现 */ } void Remove(const T& item) { /* 原有实现 */ } void Remove(const auto& id) { /* 适配实现 */ } auto begin() { return vect.begin(); } auto end() { return vect.end(); } private: std::vector<std::pair<decltype(std::declval<observerId>()), std::reference_wrapper<T>>> vect; }; // MapContainer继承CRTP接口 template <typename Key, typename Value> class MapContainer : public ContainerInterface<MapContainer<Key, Value>> { public: void Add(const Key& key, Value& value) { /* 原有实现 */ } void Remove(const Key& key) { /* 原有实现 */ } auto begin() { return container.begin(); } auto end() { return container.end(); } private: std::map<Key, std::reference_wrapper<Value>> container; }; // EventSystem使用CRTP接口 template<typename MessageType, typename ObserverType, typename observerId, typename ContainerType> class EventSystem : public Subject<MessageType, ObserverType, observerId> { public: void Attach(const observerId& observerID, ObserverType& observer) override { mContainer.Add(observerID, observer); } void Detach(const observerId& observerID) override { mContainer.Remove(observerID); } void Trigger(const MessageType& message) { for (auto& entry : mContainer) { auto& observer = entry.second; observer.get().Handle(message); } } private: ContainerType mContainer; };
关键注意事项
- 存储观察者时建议使用
std::reference_wrapper,避免拷贝观察者对象,同时需确保观察者生命周期长于事件系统 - 方案1更简洁,适合快速迭代;方案2通过CRTP实现静态多态,无运行时虚函数开销,适合性能敏感场景
- 两种方案均无需强制统一容器的参数数量,而是通过对齐
EventSystem的方法契约实现兼容
内容的提问来源于stack exchange,提问作者Fatih Sevencan
相关产品推荐
相关产品推荐

