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

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

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

ابدأ الدورة

ما هو حقن التبعيات ولماذا تحتاجه في Kotlin؟



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



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



في Kotlin، يعتبر DI مهمًا بشكل خاص لأن اللغة تُستخدم بنشاط لإنشاء تطبيقات Android، والحلول الخادمية باستخدام Ktor، والخدمات المصغرة. بدون DI، يصبح الكود مقيدًا بشدة، ويصعب اختباره وصيانته. مع DI تحصل على:

  • المرونة — استبدال تطبيق بآخر بسهولة
  • قابلية الاختبار — يمكنك إدراج كائنات mock
  • نقاء الكود — الفئات لا تعرف كيف يتم إنشاء تبعياتها


المشكلة بدون حقن التبعيات



لننظر إلى موقف نموذجي. لدينا فئة UserService تعمل مع قاعدة البيانات. بدون DI يبدو الأمر هكذا:



class UserService {    private val database = Database() // ارتباط قوي

fun getUser(id: Int): User? { return database.query("SELECT * FROM users WHERE id = $id") }}


ما هي المشاكل هنا؟ أولاً، UserService ينشئ مثيل Database بنفسه. إذا أردنا استبدال قاعدة البيانات الحقيقية بقاعدة بيانات اختبارية (مثل in-memory)، سيتعين علينا تغيير الكود داخل الفئة. ثانيًا، يصبح اختبار الوحدة مستحيلًا — لا يمكننا إدراج قاعدة بيانات وهمية. ثالثًا، تنتهك الفئة مبدأ المسؤولية الواحدة (SRP): فهي لا تعالج المستخدمين فحسب، بل تدير أيضًا إنشاء قاعدة البيانات.



تخيل أن هناك عشرات من هذه التبعيات — يتحول الكود إلى "سباغيتي" حيث كل شيء مرتبط بكل شيء. ولهذا السبب تحديدًا يوجد حقن التبعيات.



تنفيذ حقن التبعيات يدويًا



أبسط طريقة لحقن التبعيات هي تمريرها عبر المُنشئ. يُسمى هذا حقن المُنشئ. دعنا نعيد كتابة مثالنا:



class UserService(private val database: Database) {    fun getUser(id: Int): User? {        return database.query("SELECT * FROM users WHERE id = $id")    }}

// الاستخدام:val database = Database()val userService = UserService(database)


الآن UserService لا يعرف كيف يتم إنشاء Database. هو ببساطة يحصل على مثيل جاهز. يمكننا بسهولة استبدال قاعدة البيانات:



class FakeDatabase : Database() {    override fun query(sql: String): User? {        return User(1, "مستخدم اختباري")    }}

// في الاختبارات:val fakeDb = FakeDatabase()val userService = UserService(fakeDb)


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



ومع ذلك، فإن DI اليدوي له عيب: عندما يكبر المشروع، يصبح عدد التبعيات هائلًا. سيتعين عليك تتبع ترتيب إنشاء الكائنات يدويًا وتمريرها عبر عدة مستويات. هنا تأتي أطر عمل DI للمساعدة.



حقن التبعيات باستخدام Koin



Koin هو إطار عمل DI شائع لـ Kotlin، لا يتطلب توليد كود

المدونات

توصيات الكتب