حقن التبعية في C#: دليل بسيط للمبتدئين
حقن التبعية (Dependency Injection - DI) هو أحد أنماط التصميم الأساسية في التطوير الحديث باستخدام C# و.NET. إذا كنت قد بدأت للتو رحلتك في البرمجة، قد يبدو المصطلح معقدًا، لكن في الواقع الأمر أبسط بكثير. في هذه المقالة، سنشرح ما هو حقن التبعية، ولماذا هو مهم، وكيفية استخدامه عمليًا.
ما هو حقن التبعية بكلمات بسيطة؟
تخيل أنك تقوم بتجميع جهاز كمبيوتر. يمكنك شراء وحدة نظام جاهزة، حيث كل شيء ملحوم ولا يمكن استبداله. أو يمكنك تجميعه من مكونات منفصلة: معالج، بطاقة رسوميات، ذاكرة وصول عشوائي. النهج الثاني أكثر مرونة - يمكنك بسهولة استبدال بطاقة الرسوميات بأخرى أقوى أو إضافة شريحة ذاكرة إضافية.
يعمل حقن التبعية بنفس المبدأ. بدلاً من "لحام" التبعيات بشكل صارم داخل الفئة (إنشاء الكائنات مباشرة في الكود)، فإنك "تحقنها" من الخارج. هذا يجعل الكود الخاص بك مرنًا، وقابلاً للاختبار، وسهل الصيانة.
لنلقِ نظرة على مثال سيء بدون DI:
public class EmailService{ private SmtpClient _client; public EmailService() { // ارتباط صارم - الفئة تنشئ التبعية بنفسها _client = new SmtpClient("smtp.example.com", 587); } public void Send(string to, string message) { _client.Send(to, message); }}الآن نفس المثال باستخدام DI:
public class EmailService{ private readonly ISmtpClient _client; // يتم حقن التبعية عبر المُنشئ public EmailService(ISmtpClient client) { _client = client; } public void Send(string to, string message) { _client.Send(to, message); }}الفرق واضح: في الحالة الثانية، نعمل من خلال الواجهة ISmtpClient، ويتم تمرير التنفيذ المحدد من الخارج.
الطرق الأساسية لحقن التبعيات في C#
في C#، هناك ثلاث طرق رئيسية لتطبيق DI. دعنا نستعرض كل منها.
1. الحقن عبر المُنشئ
هذه هي الطريقة الأكثر شيوعًا والموصى بها. يتم تمرير التبعيات عبر معاملات المُنشئ وتخزينها في حقول خاصة.
public class OrderService{ private readonly IEmailService _emailService; private readonly ILogger _logger; public OrderService(IEmailService emailService, ILogger logger) { _emailService = emailService; _logger = logger; } public void ProcessOrder(Order order) { _logger.Log("بدأت معالجة الطلب"); _emailService.Send(order.CustomerEmail, "تم استلام طلبك"); _logger.Log("انتهت معالجة الطلب"); }}2. الحقن عبر الخاصية
يتم تعيين التبعية من خلال خاصية عامة. هذه الطريقة مفيدة عندما تكون التبعية اختيارية.
public class ReportGenerator{ public ILogger Logger { get; set; } public void GenerateReport() { Logger?.Log("جارٍ إنشاء التقرير..."); // منطق الإنشاء }}3. الحقن عبر الطريقة
يتم تمرير التبعية مباشرة إلى الطريقة التي تحتاجها. عادةً ما تُستخدم نادرًا، ولكنها قد تكون مفيدة في بعض السيناريوهات.
public class DataProcessor{ public void ProcessData(string data, IValidator validator) { if (validator.Validate(data)) { // معالجة البيانات } }}مثال عملي: من كود سيء إلى كود جيد
دعنا نرى كيف يمكن لـ DI تحسين الكود الفعلي. لنفترض أن لدينا فئة للعمل مع قاعدة البيانات.
كود سيء (بدون DI):
public class UserRepository{ private SqlConnect