حقن التبعيات في JavaScript: دليل شامل للمبتدئين

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

مقدمة: ما هو حقن التبعيات ولماذا هو مطلوب؟

\n\n

تخيل أنك تقوم بتجميع جهاز كمبيوتر. بدلاً من شراء جهاز متكامل جاهز، تختار بشكل منفصل معالجًا وبطاقة رسوميات وذاكرة وصول عشوائي وتقوم بتوصيلها باللوحة الأم. إذا أردت ترقية بطاقة الرسوميات، لن تضطر إلى تغيير الكمبيوتر بأكمله - بل ستستبدل مكونًا واحدًا فقط. حقن التبعيات (DI) في البرمجة يعمل بنفس المبدأ.

\n\n

حقن التبعيات (Dependency Injection) هو نمط تصميم يحصل فيه الكائن على تبعياته من الخارج، بدلاً من إنشائها بنفسه. في سياق JavaScript و Node.js، هذا يعني أن الفئة أو الدالة لا تقوم بإنشاء نسخ من الفئات الأخرى داخلها، بل تستقبلها عبر المُنشئ أو المعاملات.

\n\n

هذا النهج هو جزء من مبدأ عكس التبعيات (Dependency Inversion Principle) من SOLID ويجعل الكود أكثر مرونة وقابلية للاختبار والصيانة. في هذه المقالة، سنشرح DI بأمثلة عملية للمطورين المبتدئين.

\n\n

المشكلة: الاقتران القوي للكود

\n\n

لننظر إلى حالة نموذجية في تطبيق Node.js. لنفترض أن لدينا خدمة لإرسال إشعارات البريد الإلكتروني:

\n\n
// بدون حقن التبعيات\nclass EmailService {\n  sendEmail(to, subject, body) {\n    console.log(`إرسال بريد إلكتروني إلى ${to}: ${subject}`);\n    // منطق الإرسال الفعلي\n  }\n}\n\nclass UserService {\n  constructor() {\n    this.emailService = new EmailService(); // ❌ اقتران قوي\n  }\n\n  registerUser(email) {\n    // منطق تسجيل المستخدم\n    this.emailService.sendEmail(email, 'مرحبًا بك!', '...');\n  }\n}\n\nconst userService = new UserService();\nuserService.registerUser('user@example.com');\n
\n\n

ما هي المشكلة؟ الفئة UserService مرتبطة بقوة بتطبيق معين لـ EmailService. إذا أردت:

\n
    \n
  • استبدال إرسال البريد الإلكتروني بإرسال رسائل نصية (SMS)
  • \n
  • إضافة تسجيل لجميع الرسائل المرسلة
  • \n
  • اختبار UserService دون إرسال رسائل حقيقية
  • \n
\n\n

سيتعين عليك إعادة كتابة كود UserService. هذا ينتهك مبدأ الانفتاح/الإغلاق (Open/Closed Principle).

\n\n

الحل: إدخال التبعيات عبر المُنشئ

\n\n

الطريقة الأولى والأبسط لإدخال التبعيات هي تمريرها عبر مُنشئ الفئة. دعنا نعيد كتابة مثالنا:

\n\n
// مع حقن التبعيات\nclass EmailService {\n  sendEmail(to, subject, body) {\n    console.log(`إرسال بريد إلكتروني إلى ${to}: ${subject}`);\n  }\n}\n\nclass SmsService {\n  sendSms(phone, message) {\n    console.log(`إرسال رسالة نصية إلى ${phone}: ${message}`);\n  }\n}\n\nclass UserService {\n  constructor(notificationService) {\n    this.notificationService = notificationService; // ✅ إدخال التبعية\n  }\n\n  registerUser(contact, isPhone = false) {\n    // منطق التسجيل\n    if (isPhone) {\n      this.notificationService.sendSms(contact, 'مرحبًا بك!');\n    } else {\n      this.notificationService.sendEmail(contact, 'مرحبًا بك!', '...');\n    }\n  }\n}\n\n// الاستخدام\nconst emailService = new EmailService();\nconst userServiceWithEmail = new UserService(emailService);\nuserServiceWithEmail.registerUser('user@example.com');\n\nconst smsService = new SmsService();\nconst userServiceWithSms = new UserService(smsService);\nuserServiceWithSms.registerUser('+1234567890', true);\n
\n\n

الآن UserService لا يعرف أي خدمة محددة تُستخدم - بريد إلكتروني أم رسالة نصية. هو فقط يستدعي الدالة sendEmail أو sendSms على الكائن المُمرر. هذا يُسمى عكس التبعيات: الوحدة عالية المستوى لا تعتمد على الوحدة منخفضة المستوى، كلاهما يعتمدان على التجريد.

\n\n

مزايا هذا النهج:

\n
    \n
  • المرونة: سهولة الاستبدال

المدونات

توصيات الكتب