حقن التبعية في Kotlin: دليل بسيط للمبتدئين

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

مقدمة: ما هو حقن التبعية ولماذا هو مطلوب؟

\n

تخيل أنك تبني منزلاً. أنت بحاجة إلى الطوب والأسمنت والأدوات. بدلاً من الذهاب إلى المستودع في كل مرة، يمكنك ببساطة طلب توصيل جميع المواد إلى موقع البناء. حقن التبعية (Dependency Injection) أو DI هو نفس المبدأ، ولكن لكودك.

\n

حقن التبعية هو نمط تصميم حيث يتلقى الكائن تبعياته من الخارج، بدلاً من إنشائها بنفسه. وهذا يجعل الكود أكثر مرونة وقابلية للاختبار وسهولة في الصيانة. في هذه المقالة، سنشرح DI باستخدام Kotlin - لغة برمجة حديثة مثالية لتعلم هذا النمط.

\n\n

المشكلة: لماذا يصبح الكود هشاً بدون DI؟

\n

لنأخذ مثالاً نموذجياً. لنفترض أن لدينا كلاس UserService يرسل إشعارات البريد الإلكتروني. بدون DI، سيبدو هكذا:

\n
class EmailService {\n    fun sendEmail(to: String, message: String) {\n        println("إرسال بريد إلكتروني إلى $to: $message")\n    }\n}\n\nclass UserService {\n    private val emailService = EmailService() // ارتباط صارم!\n\n    fun notifyUser(email: String) {\n        emailService.sendEmail(email, "مرحباً أيها المستخدم!")\n    }\n}
\n

ما هي المشكلة؟ UserService مرتبط بشكل صارم بتنفيذ محدد لـ EmailService. إذا أردت:

\n
    \n
  • استبدال EmailService بـ SmsService؛
  • \n
  • إضافة تسجيل (logging)؛
  • \n
  • كتابة اختبارات وحدة باستخدام كائن وهمي (mock)،
  • \n
\n

— سيتعين عليك تغيير كود UserService. وهذا ينتهك مبدأ Open/Closed (الانفتاح/الإغلاق) ويجعل الاختبار أكثر صعوبة.

\n\n

الحل: حقن التبعية يدوياً في Kotlin

\n

أبسط طريقة لتنفيذ DI هي تمرير التبعية عبر المُنشئ. في Kotlin، يتم ذلك بشكل موجز جداً:

\n
// 1. إنشاء واجهة (تجريد)\ninterface NotificationService {\n    fun send(to: String, message: String)\n}\n\n// 2. تنفيذ خدمات محددة\nclass EmailService : NotificationService {\n    override fun send(to: String, message: String) {\n        println("بريد إلكتروني إلى $to: $message")\n    }\n}\n\nclass SmsService : NotificationService {\n    override fun send(to: String, message: String) {\n        println("رسالة نصية إلى $to: $message")\n    }\n}\n\n// 3. حقن التبعية عبر المُنشئ\nclass UserService(private val notificationService: NotificationService) {\n    fun notifyUser(email: String) {\n        notificationService.send(email, "مرحباً!")\n    }\n}\n\n// 4. الاستخدام\nfun main() {\n    val emailService = EmailService()\n    val userService = UserService(emailService) // حقن!\n    userService.notifyUser("user@example.com")\n}
\n

مزايا هذا النهج:

\n
    \n
  • المرونة: يمكن بسهولة استبدال EmailService بـ SmsService؛
  • \n
  • قابلية الاختبار: في الاختبارات، يمكنك تمرير كائن وهمي:
  • \n
\n
class MockNotificationService : NotificationService {\n    var lastMessage: String? = null\n    override fun send(to: String, message: String) {\n        lastMessage = message\n    }\n}\n\nfun test() {\n    val mock = MockNotificationService()\n    val userService = UserService(mock)\n    userService.notifyUser("test@test.com")\n    assert(mock.lastMessage == "مرحباً!")\n}
\n\n

أطر عمل DI: Koin و Dagger لـ Kotlin

\n

مع DI اليدوي، إذا كان المشروع كبيراً، ستكتب الكثير من الكود المتكرر. هنا تأتي أطر عمل DI للمساعدة. في عالم Kotlin، هناك اثنان مشهوران: Koin (بسيط) و Dagger/Hilt (قوي).

\n\n

Koin: البساطة والسرعة

\n

Koin هو إطار عمل DI خفيف الوزن، مكتوب بلغة Kotlin. لا يتطلب توليد كود وي

المدونات

توصيات الكتب