حقن التبعية في Java: عمليًا
\n\nحقن التبعية (Dependency Injection - DI) هو أحد المبادئ الأساسية في التطوير الحديث بلغة Java، ويشكل أساس أطر العمل مثل Spring و Jakarta EE. في هذه المقالة، سنشرح ما هو حقن التبعية، ولماذا هو مهم، والأهم من ذلك، كيفية تطبيقه عمليًا مع أمثلة الكود.
\n\nما هو حقن التبعية ولماذا هو مهم؟
\n\nحقن التبعية هو نمط تصميم يتم فيه توفير التبعيات للكائن من الخارج، بدلاً من أن يقوم الكائن بإنشائها بنفسه. هذا يحقق مبدأ انعكاس التحكم (Inversion of Control - IoC). بدلاً من أن تدير الفئة تبعياتها بنفسها، يتم نقل التحكم إلى حاوية خارجية أو مصنع.
\n\nتخيل أنك تكتب تطبيقًا لإرسال الإشعارات. بدون DI، قد يبدو الكود الخاص بك كالتالي:
\n\npublic class NotificationService {\n private EmailSender emailSender = new EmailSender();\n\n public void send(String message) {\n emailSender.send(message);\n }\n}\n\nالمشكلة واضحة: NotificationService مرتبط بشكل صارم بـ EmailSender. إذا أردت إضافة إشعارات SMS أو Push، فسيتعين عليك إعادة كتابة الفئة بأكملها. مع حقن التبعية، يتم حل هذه المشكلة:
public class NotificationService {\n private final MessageSender messageSender;\n\n // يتم تمرير التبعية عبر المُنشئ\n public NotificationService(MessageSender messageSender) {\n this.messageSender = messageSender;\n }\n\n public void send(String message) {\n messageSender.send(message);\n }\n}\n\nالآن تعتمد الفئة على التجريد MessageSender، وليس على تنفيذ محدد. هذا يجعل الكود مرنًا وقابلًا للاختبار وقابلًا للتوسع.
طرق حقن التبعية في Java
\n\nهناك ثلاث طرق رئيسية لتنفيذ DI في Java. دعنا نستعرض كل منها مع أمثلة.
\n\n1. الحقن عبر المُنشئ (Constructor Injection)
\n\nهذه هي الطريقة الأكثر تفضيلاً. يتم تمرير التبعيات عبر معاملات المُنشئ وعادةً ما يتم تعليمها كـ final.
public class UserService {\n private final UserRepository userRepository;\n private final EmailService emailService;\n\n public UserService(UserRepository userRepository, EmailService emailService) {\n this.userRepository = userRepository;\n this.emailService = emailService;\n }\n\n public void registerUser(User user) {\n userRepository.save(user);\n emailService.sendWelcomeEmail(user.getEmail());\n }\n}\n\nالمزايا:
\n- \n
- التبعيات الإلزامية واضحة بشكل صريح \n
- الكائن دائمًا في حالة صالحة \n
- مناسب جدًا للكائنات غير القابلة للتغيير (immutable) \n
- سهولة الاختبار —只需 تمرير كائنات وهمية (mock) \n
2. الحقن عبر الضابط (Setter Injection)
\n\nيتم تعيين التبعيات عبر طرق الضابط (setter) بعد إنشاء الكائن.
\n\npublic class OrderService {\n private PaymentGateway paymentGateway;\n\n public void setPaymentGateway(PaymentGateway paymentGateway) {\n this.paymentGateway = paymentGateway;\n }\n\n public void processOrder(Order order) {\n paymentGateway.charge(order.getTotal());\n }\n}\n\nمتى تستخدم: للتبعيات الاختيارية أو عندما يمكن أن تتغير التبعيات خلال عمر الكائن. ومع ذلك، هذه الطريقة تجعل الكائن قابلًا للتغيير وأقل قابلية للتنبؤ.
\n\n3. الحقن عبر الحقل (Field Injection)
\n\nغالبًا ما يُستخدم مع أطر العمل من خلال التعليقات التوضيحية، مثل @Autowired في Spring.
public class ReportService {\n @Autowired\n private DataSource dataSource;\n\n public Report generateReport() {\n return dataSource