مقدمة: ما هو Dependency Injection ولماذا هو ضروري في Java
في تطوير Java الحديث، نقاء الكود وقابليته للاختبار من المتطلبات الأساسية للمحترف. Dependency Injection (DI)، أو حقن التبعيات، هو نمط تصميم يسمح لك بجعل كودك ضعيف الاقتران، مرنًا، وسهل الصيانة. بدلاً من أن يقوم الكائن بإنشاء تبعياته بنفسه (على سبيل المثال، عبر new)، يقوم DI «بحقنها» من الخارج. هذا جزء أساسي من مبدأ عكس التحكم (IoC).
في هذه المقالة، سنشرح كيفية عمل Dependency Injection في Java، ونستعرض ثلاث طرق رئيسية لتنفيذه (عبر المُنشئ، والـ setters، والواجهة)، ونوضح كيف يتم تطبيق DI في المشاريع الحقيقية، بما في ذلك إطار العمل الشهير Spring.
لماذا يصبح كودك هشًا بدون DI؟
تخيل كلاس Car الذي يقوم بإنشاء محرك بداخله:
public class Car {
private Engine engine;
public Car() {
this.engine = new PetrolEngine(); // ارتباط صلب
}
public void start() {
engine.run();
}
}
مشاكل هذا النهج واضحة:
- عدم قابلية إعادة الاستخدام: لن تتمكن بسهولة من استبدال
PetrolEngineبـElectricEngine. - صعوبة الاختبار: لاختبارات الوحدة، ستحتاج إلى إنشاء محرك حقيقي بدلاً من كائن وهمي (mock).
- انتهاك مبدأ المسؤولية الواحدة (SRP): كلاس
Carلا يهتم فقط بإدارة السيارة، بل أيضًا بإنشاء المحرك.
يحل Dependency Injection هذه المشاكل عن طريق تفويض إنشاء التبعيات إلى كود خارجي (حاوية أو كلاس آخر).
ثلاث طرق لحقن التبعيات في Java
في Java، هناك ثلاث طرق رئيسية لتنفيذ DI. دعنا نستعرض كل منها باستخدام مثال نظام الإشعارات.
1. الحقن عبر المُنشئ (Constructor Injection)
هذه هي الطريقة الأكثر تفضيلاً. يتم تمرير التبعيات عبر معاملات المُنشئ. هذا يضمن أن الكائن سيتم إنشاؤه دائمًا في حالة صالحة وأن التبعيات لا يمكن تغييرها بعد الإنشاء (الثبات).
public interface MessageService {
void sendMessage(String message, String recipient);
}
public class EmailService implements MessageService {
@Override
public void sendMessage(String message, String recipient) {
System.out.println("Email sent to " + recipient + ": " + message);
}
}
public class NotificationSender {
private final MessageService messageService;
// حقن التبعية عبر المُنشئ
public NotificationSender(MessageService messageService) {
this.messageService = messageService;
}
public void notifyUser(String userEmail, String msg) {
messageService.sendMessage(msg, userEmail);
}
}
// الاستخدام
public class Main {
public static void main(String[] args) {
MessageService emailService = new EmailService();
NotificationSender sender = new NotificationSender(emailService); // DI
sender.notifyUser("user@example.com", "Hello!");
}
}
2. الحقن عبر الـ setters (Setter Injection)
مناسب للتبعيات الاختيارية أو عندما يمكن تغيير التبعية خلال عمر الكائن. ومع ذلك، قد يتم إنشاء الكائن في حالة غير مكتملة إذا نسيت استدعاء الـ setter.
public class NotificationSender {
private MessageService messageService;
// Setter لحقن التبعية
public void setMessageService(MessageService messageService) {
this.messageService = messageService;
}
public void notifyUser(String userEmail, String msg) {
if (messageService != null) {
messageService.sendMessage(msg, userEmail);
} else {
System.o
```