أساسيات Dependency Injection
جزء من قسم البرمجة كائنية التوجه في رحلة C# على Coddy. الدرس 46 من 70.
حقن الاعتماديات (DI) هو أسلوب تتلقى فيه الفئة اعتمادياتها من خارجها بدلًا من إنشائها بنفسها. وهذا يجعل الشيفرة أكثر مرونة وقابلية للاختبار وأقل اقترانًا.
بدون DI، تنشئ class تبعياتها بنفسها، مما يجعل تغييرها أو اختبارها أمرًا صعبًا:
public class OrderService
{
private EmailSender sender = new EmailSender(); // مقترن بإحكام
public void PlaceOrder() => sender.Send("Order placed");
}باستخدام DI، يتم تمرير التبعيات من خلال المُنشئ:
public interface IMessageSender
{
void Send(string message);
}
public class EmailSender : IMessageSender
{
public void Send(string message) => Console.WriteLine("Email: " + message);
}
public class OrderService
{
private IMessageSender sender;
public OrderService(IMessageSender sender)
{
this.sender = sender;
}
public void PlaceOrder() => sender.Send("Order placed");
}الآن لا يعرف OrderService ولا يهتم بما إذا كان يستخدم البريد الإلكتروني أو SMS أو مرسلًا وهميًا للاختبار. يتم إجراء injected لـ dependency عند إنشاء الكائن:
IMessageSender emailSender = new EmailSender();
OrderService service = new OrderService(emailSender);
service.PlaceOrder(); // البريد الإلكتروني: تم تقديم الطلبيتبع هذا النمط مبدأ الاعتماد على التجريدات (الواجهات) بدلًا من التنفيذات الملموسة. ويتيح تبديل التنفيذات دون تعديل الفئة التي تستخدمها، وهو أمر ضروري لاختبار الوحدات والحفاظ على بنيات مرنة.
التحدي
سهللنَبْنِ نظامًا لتسجيل السجلات يوضّح قوة حقن الاعتماديات. بدلًا من ترميز كيفية كتابة السجلات بشكل ثابت، ستنشئ فئة Logger مرنة يمكنها العمل مع أي وجهة لتسجيل السجلات - وحدة التحكم، أو محاكاة ملف، أو أي شيء آخر - من دون معرفة التفاصيل.
ستنظّم الشيفرة عبر ثلاثة ملفات:
ILogWriter.cs: عرّف واجهة باسمILogWriterفي مساحة الأسماءLogging. تمثّل هذه الواجهة العقد الخاص بأي وجهة لتسجيل السجلات، ويجب أن تحتوي علىmethodواحدةWrite(string message)تُرجع سلسلة تصف المكان الذي كُتبت فيه الرسالة.LogWriters.cs: أنشئ تطبيقين ملموسين للواجهةILogWriterفي مساحة الأسماءLogging:ConsoleLogWriter- تُرجعWriteالخاصة بهاConsole: {message}FileLogWriter- تتلقى اسم ملف من خلالconstructorالخاصة بها، وتُرجعWriteالخاصة بهاFile[{filename}]: {message}
Logger.cs: أنشئ فئةLoggerفي مساحة الأسماءLogging، وتتلقى dependency الخاصة بها من خلال حقن الاعتمادية فيconstructor. يجب أن تقومLoggerبما يلي:- قبول
ILogWriterمن خلالconstructorالخاصة بها وتخزينه فيfieldخاصة - امتلاك
methodباسمLog(string message)تفوّض المهمة إلى الكاتبinjectedوتُرجع النتيجة
- قبول
Program.cs: وضّح حقن الاعتماديات من خلال إنشاء كاتبي سجلات مختلفين - أحدهما باستخدامConsoleLogWriterوالآخر باستخدامFileLogWriter. وضّح كيف تتصرف الفئة نفسهاLoggerبشكل مختلف بناءً على الاعتمادية التي يتم حقنها.
ستتلقى مدخلين:
- اسم الملف الخاص بكاتب السجلات للملف
- الرسالة المراد تسجيلها
أنشئ Logger باستخدام ConsoleLogWriter وسجّل الرسالة. ثم أنشئ Logger آخر باستخدام FileLogWriter (مع استخدام اسم الملف من الإدخال) وسجّل الرسالة نفسها. اطبع كل نتيجة في سطر خاص بها.
على سبيل المثال، إذا كان المدخلان هما app.log وSystem started، فيجب أن يكون الناتج:
Console: System started
File[app.log]: System startedلاحظ أن فئة Logger لا تتغير أبدًا - فهي تعمل مع أي تطبيق للواجهة ILogWriter. هذا هو جوهر حقن الاعتماديات: تعتمد Logger على تجريد (الواجهة)، ويجري "injected" التطبيق الملموس من الخارج. يمكنك بسهولة إضافة DatabaseLogWriter أو CloudLogWriter لاحقًا من دون تعديل فئة Logger على الإطلاق!
جرّب بنفسك
using System;
using Logging;
class Program
{
public static void Main(string[] args)
{
// اقرأ المدخلات
string filename = Console.ReadLine();
string message = Console.ReadLine();
// TODO: أنشئ Logger مع ConsoleLogWriter وسجّل الرسالة
// اطبع النتيجة
// TODO: أنشئ Logger آخر مع FileLogWriter (باستخدام filename) وسجّل نفس الرسالة
// اطبع النتيجة
}
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
1أساسيات OOP
الملفات الخارجيةNamespaces والتوجيهاتمقدمة في Classes و Objectsالكلمة المفتاحية 'this'Methods والمعاملاتFields مقابل PropertiesConstructorsObject Initializersمراجعة - آلة حاسبة بسيطة4الوراثة
صيغة الوراثة الأساسية (:)الكلمة المفتاحية 'base'الكلمات المفتاحية Virtual و Overrideالأصناف المختومة (Sealed Classes)الفئة الأساسية 'object'مراجعة - هيكلية الموظفين7ميزات متقدمة
تحميل العوامل (Operator Overloading)المفهرسات (Indexers (this[]))إعادة تعريف ()ToStringدوال التوسعة (Extension Methods)ملخص - Custom List2الخصائص والأعضاء الساكنة (Static Members)
الخصائص المطبقة تلقائيًا (Auto-Implemented Properties)خصائص القراءة فقط والكتابة فقطالحقول والدوال الساكنة (Static)الفئات الساكنة (Static Classes)الأعضاء ذات البنية التعبيرية (Expression-Bodied Members)5تعدد الأشكال والواجهات (Polymorphism & Interfaces)
تعدد الأشكال: وقت التجميع مقابل وقت التشغيلالواجهة مقابل الفئة المجردة (Abstract Class)الواجهات المتعددةالواجهات الصريحةUpcasting و Downcastingملخص - حاسبة الأشكال8مفاهيم البرمجة كائنية التوجه (OOP) المتقدمة
التركيب بدلاً من الوراثةالـ Generics (الأصناف والدوال)الـ Delegates والأحداثالـ Attributes والـ Reflectionواجهة IDisposable وجملة usingأساسيات Dependency Injectionتدرّب بنفسك: مترجم C# عبر الإنترنت