ما هو حقن التبعيات (DI) في C# ولماذا هو ضروري؟
\n\nحقن التبعيات (Dependency Injection) هو نمط تصميم يسمح بجعل الكود أكثر مرونة وقابلية للاختبار والصيانة. بدلاً من أن يقوم الفصل (class) بإنشاء الكائنات التي يعتمد عليها بنفسه، يتم "حقن" هذه الكائنات من الخارج. هذا أحد المبادئ الأساسية التي تقوم عليها التطوير الحديث باستخدام C# و.NET.
\n\nتخيل أنك تكتب تطبيقًا لإرسال إشعارات البريد الإلكتروني. إذا كان الفصل الخاص بك NotificationService سيقوم بإنشاء مثيل مباشر من SmtpClient، فعند تغيير خادم البريد أو الحاجة لاستخدام طريقة إرسال أخرى (مثل عبر API)، سيتعين عليك إعادة كتابة الكود. DI يحل هذه المشكلة.
المشكلة بدون حقن التبعيات
\n\nلننظر إلى مثال نموذجي لكود سيء حيث يتم إنشاء التبعية داخل الفصل:
\n\npublic class EmailService\n{\n private SmtpClient _smtpClient;\n\n public EmailService()\n {\n // ربط صارم بتطبيق محدد\n _smtpClient = new SmtpClient("smtp.gmail.com", 587);\n }\n\n public void SendEmail(string to, string subject, string body)\n {\n // منطق الإرسال\n }\n}\n\npublic class NotificationService\n{\n private EmailService _emailService;\n\n public NotificationService()\n {\n _emailService = new EmailService(); // ارتباط صارم\n }\n\n public void NotifyUser(string email)\n {\n _emailService.SendEmail(email, "إشعار", "مرحبًا!");\n }\n}\n\nعيوب هذا الأسلوب:
\n- \n
- الارتباط العالي (tight coupling): الفصول
NotificationServiceوEmailServiceمرتبطة ببعضها البعض بشكل صارم. \n - صعوبة الاختبار: من المستحيل استبدال
EmailServiceبكائن وهمي (mock) أثناء اختبارات الوحدة. \n - صعوبة التوسع: إذا كنت بحاجة لإضافة إشعارات SMS، فسيتعين عليك تغيير المنطق داخل
NotificationService. \n
تطبيق حقن التبعيات في C#
\n\nDI يحل هذه المشكلات باستخدام ثلاثة أساليب رئيسية: الحقن عبر المُنشئ، وعبر الخاصية، وعبر الطريقة. الأكثر شيوعًا والموصى به هو الحقن عبر المُنشئ (Constructor Injection).
\n\nالخطوة 1. إنشاء واجهة
\nأولاً، نحدد تجريدًا (واجهة) يصف العقد (contract) لإرسال الرسائل:
\n\npublic interface IMessageSender\n{\n void Send(string to, string message);\n}\n\nالخطوة 2. تطبيق الخدمات المحددة
\nالآن، سننشئ عدة تطبيقات لهذه الواجهة:
\n\npublic class EmailSender : IMessageSender\n{\n public void Send(string to, string message)\n {\n Console.WriteLine($"إرسال بريد إلكتروني إلى {to}: {message}");\n }\n}\n\npublic class SmsSender : IMessageSender\n{\n public void Send(string to, string message)\n {\n Console.WriteLine($"إرسال SMS إلى {to}: {message}");\n }\n}\n\nالخطوة 3. تطبيق DI عبر المُنشئ
\nالآن، الفصل NotificationService لا يقوم بإنشاء التبعيات بنفسه، بل يستقبلها عبر المُنشئ:
public class NotificationService\n{\n private readonly IMessageSender _messageSender;\n\n // حقن التبعية عبر المُنشئ\n public NotificationService(IMessageSender messageSender)\n {\n _messageSender = messageSender;\n }\n\n public void Notify(string userContact, string message)\n {\n _messageSender.Send(userContact, message);\n }\n}\n\nالآن يمكننا بسهولة تبديل التطبيقات:
\n\n// استخدام البريد الإلكتروني\nvar emailSender = new EmailSender();\nvar emailNotifier = new NotificationService(emailSender);\nemailNotifier.Notify("user@example.com", "مرحبًا عبر البريد الإلكتروني!");\n\n// استخدام SMS\nvar sms