حاوية Dependency Injection للغة C++: نظرة عامة وأمثلة عملية
\n\nفي التطوير الحديث بلغة C++، يتم بشكل متزايد تطبيق مبادئ Inversion of Control (IoC) و Dependency Injection (DI). تسمح هذه الأنماط بإنشاء كود ضعيف الاقتران، قابل للاختبار، وسهل التوسيع. على عكس اللغات ذات الذاكرة المُدارة (Java، C#)، لا تحتوي C++ على انعكاس (Reflection) مدمج، لذلك يتطلب تنفيذ حاويات DI هنا برمجة وصفية للقوالب (Template Metaprogramming) واستخدام المعايير الحديثة (C++11/14/17/20).
\n\nفي هذه المقالة، سنستعرض ثلاثة أساليب شائعة لحقن التبعيات في C++: المكتبات Boost.DI و Google Fruit و Kangaru، بالإضافة إلى كتابة حاوية بسيطة خاصة بنا لفهم الآلية.
\n\nما هو Dependency Injection؟
\n\nDependency Injection هي تقنية يحصل فيها الكائن على تبعياته من الخارج، بدلاً من إنشائها بنفسه. الأنواع الرئيسية لـ DI:
\n- \n
- Constructor Injection — يتم تمرير التبعيات عبر المُنشئ (Constructor). \n
- Setter Injection — يتم تعيين التبعيات عبر دوال الضبط (Setters). \n
- Interface Injection — يتم تمرير التبعيات عبر واجهة (Interface). \n
مثال بدون DI (اقتران قوي):
\nclass Logger {\npublic:\n void log(const std::string& msg) { /* كتابة إلى ملف */ }\n};\n\nclass Service {\n Logger logger; // تبعية قوية\npublic:\n void doWork() { logger.log("Work done"); }\n};\n\nمثال مع DI (اقتران ضعيف):
\nclass ILogger {\npublic:\n virtual ~ILogger() = default;\n virtual void log(const std::string& msg) = 0;\n};\n\nclass ConsoleLogger : public ILogger {\npublic:\n void log(const std::string& msg) override { std::cout << msg << std::endl; }\n};\n\nclass Service {\n std::shared_ptr<ILogger> logger;\npublic:\n explicit Service(std::shared_ptr<ILogger> l) : logger(std::move(l)) {}\n void doWork() { logger->log("Work done"); }\n};\n\nنظرة عامة على حاويات DI للغة C++
\n\n1. Boost.DI
\nBoost.DI — واحدة من أشهر المكتبات. تستخدم قوالب C++14/17 لحل التبعيات تلقائياً. تدعم تعدد الأشكال (Polymorphism)، ونطاقات الرؤية (singleton, unique)، والتكوين المعياري (Modular Configuration).
\n\nمثال على الاستخدام:
\n#include <boost/di.hpp>\n#include <iostream>\n\nnamespace di = boost::di;\n\nclass IWriter {\npublic:\n virtual ~IWriter() = default;\n virtual void write(const std::string&) = 0;\n};\n\nclass ConsoleWriter : public IWriter {\npublic:\n void write(const std::string& msg) override {\n std::cout << msg << std::endl;\n }\n};\n\nclass App {\n std::shared_ptr<IWriter> writer;\npublic:\n explicit App(std::shared_ptr<IWriter> w) : writer(std::move(w)) {}\n void run() { writer->write("Hello from Boost.DI!"); }\n};\n\nint main() {\n auto injector = di::make_injector(\n di::bind<IWriter>.to<ConsoleWriter>()\n );\n auto app = injector.create<std::unique_ptr<App>>();\n app->run();\n return 0;\n}\n\nالإيجابيات: كتابة قوية (Strong Typing)، حمل زمن تشغيل ضئيل (Minimal Runtime Overhead)، دعم C++14/17.
\nالسلبيات: رسائل خطأ مترجم معقدة، زيادة وقت البناء (Build Time).
\n\n2. Google Fruit
\nGoogle Fruit — حاوية من Google، تعمل على C++11. تستخدم وحدات ماكرو (Macros) لتسجيل المكونات وتدعم الحقن عبر المُنشئ.
\n\nمثال:
\n#include <fruit/fruit.h>\n#include <iostream>\n\nclass IWriter {\npublic:\n virtual void write(const std::string&) = 0;\n};\n\nclass ConsoleWriter : public IWriter {\npubli