ما هو حقن التبعية في C++ ولماذا هو مهم؟
\n\nحقن التبعية (Dependency Injection - DI) هو أحد المبادئ الأساسية في البرمجة كائنية التوجه، والذي يسمح بجعل الكود أكثر مرونة، قابلية للاختبار، وسهولة في الصيانة. إذا كنت مبتدئًا في C++ وسمعت مصطلحات مثل "الاقتران الضعيف" أو "عكس التحكم" ولكنك لم تفهمها تمامًا، فهذه المقالة لك.
\n\nبكلمات بسيطة، حقن التبعية (Dependency Injection) هو تقنية يحصل فيها الكائن على تبعياته من الخارج، بدلاً من إنشائها بنفسه. بدلاً من أن تقوم الفئة بإنشاء كائنات من فئات أخرى، يتم "حقن" هذه الكائنات فيها عبر المُنشئ، أو دالة الضبط، أو الواجهة.
\n\nالمشكلة: الاقتران القوي (tight coupling)
\n\nتخيل أنك تكتب تطبيقًا لإرسال الإشعارات. بدون DI، قد يبدو الكود الخاص بك كالتالي:
\n\n#include <iostream>\n#include <string>\n\nclass EmailService {\npublic:\n void send(const std::string& message) {\n std::cout << "إرسال بريد إلكتروني: " << message << std::endl;\n }\n};\n\nclass Notification {\nprivate:\n EmailService emailService; // تبعية صلبة!\npublic:\n void notify(const std::string& msg) {\n emailService.send(msg);\n }\n};\n\nint main() {\n Notification notif;\n notif.notify("مرحبًا، أيها العالم!");\n return 0;\n}\n\nفي هذا المثال، الفئة Notification مرتبطة بشدة بالفئة EmailService. إذا أردت إضافة إشعارات SMS أو تسجيل، فسيتعين عليك إعادة كتابة الفئة Notification. يصعب اختبار هذا الكود، توسيعه، وصيانته.
الحل: حقن التبعية (Dependency Injection)
\n\nلحل مشكلة الاقتران القوي، نستخدم DI. الفكرة الأساسية هي استخراج واجهة (فئة مجردة) لخدمة الإشعارات وتمريرها إلى الفئة Notification من الخارج.
الخطوة 1: إنشاء واجهة مجردة
\n\n#include <iostream>\n#include <string>\n#include <memory>\n\n// واجهة مجردة لجميع خدمات الإشعارات\nclass IMessageService {\npublic:\n virtual ~IMessageService() = default;\n virtual void send(const std::string& message) = 0;\n};\n\nالخطوة 2: تنفيذ خدمات محددة
\n\nclass EmailService : public IMessageService {\npublic:\n void send(const std::string& message) override {\n std::cout << "📧 بريد إلكتروني: " << message << std::endl;\n }\n};\n\nclass SMSService : public IMessageService {\npublic:\n void send(const std::string& message) override {\n std::cout << "📱 رسالة نصية: " << message << std::endl;\n }\n};\n\nالخطوة 3: حقن التبعية عبر المُنشئ
\n\nclass Notification {\nprivate:\n std::unique_ptr<IMessageService> service; // التبعية تُحقن من الخارج\npublic:\n // الحقن عبر المُنشئ (Constructor Injection)\n Notification(std::unique_ptr<IMessageService> svc) \n : service(std::move(svc)) {}\n \n void notify(const std::string& msg) {\n service->send(msg);\n }\n};\n\nint main() {\n // إنشاء تنفيذ محدد\n auto emailService = std::make_unique<EmailService>();\n \n // حقنها في Notification\n Notification notif(std::move(emailService));\n notif.notify("حقن التبعية يعمل!");\n \n // تغيير سهل إلى SMS\n auto smsService = std::make_unique<SMSService>();\n Notification smsNotif(std::move(smsService));\n smsNotif.notify("الآن عبر SMS!");\n \n return 0;\n}\n\nثلاث طرق رئيسية لحقن التبعيات في C++
\n\n1. Constructor Injection (عبر المُنشئ)
\nالطريقة الأكثر شيوعًا والموصى بها. يتم تمرير التبعيات عند إنشاء الكائن وتبقى ثابتة طوال عمره.
\n\n<