Event Bus ونمط Observer في C++: من النظرية إلى التنفيذ الحديث
في التطوير الحديث بلغة C++، تظهر الحاجة بشكل متزايد إلى آلية مرنة لتبادل الرسائل بين المكونات التي لا ينبغي أن تعرف بعضها البعض بشكل مباشر. نمط Observer وتطوره - Event Bus - يحل هذه المشكلة، مما يسمح ببناء أنظمة ضعيفة الارتباط وقابلة للتوسع. في هذه المقالة، سنشرح كيفية تنفيذ Event Bus بلغة C++ باستخدام المكتبة القياسية (C++17/20)، وسنستعرض أمثلة عملية.
نمط Observer: الأساس الكلاسيكي
يحدد نمط Observer علاقة "واحد إلى متعدد" بين الكائنات: عندما يغير كائن واحد (الموضوع) حالته، يتم إخطار جميع الكائنات التابعة (المراقبون) تلقائيًا بذلك. في 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، ويجب أن تكون الرسائل سلاسل نصية. في الممارسة العملية، هناك حاجة إلى نظام أكثر مرونة.
Event Bus: التطور الحديث
Event Bus هو ناقل مركزي يمكن من خلاله للمكونات نشر الأحداث والاشتراك فيها. الاختلافات الرئيسية عن Observer الكلاسيكي:
- الأحداث المُنَمَّطَة - لكل حدث نوع بيانات خاص به.
- غياب التسلسل الهرمي الصارم - المشتركون ليسوا مضطرين لوراثة واجهة مشتركة.
- ناقل عام أو محلي - يمكن أن يكون لديك Event Bus واحد للتطبيق بأكمله أو عدة ناقلات معزولة.
تنفيذ Event Bus باستخدام C++17/20 مع std::any
نستخدم std::any لتخزين بيانات من أي نوع و std::function للمعالجات. لفصل الأحداث حسب الأنواع، نطبق std::type_index.
#include <iostream>#include <any>#include <function