مقدمة: عامل صغير واحد - سحر كبير
\n\nهل سبق لك أن نظرت إلى كود شخص آخر بلغة Kotlin ورأيت بناءً غريبًا مثل val name by lazy { ... } أو class Derived(Base by impl)؟ إذا كان الأمر كذلك، فمن المحتمل أنك تساءلت: «ما الذي تفعله الكلمة المفتاحية by في Kotlin؟». هذا السؤال هو أحد أكثر الأسئلة شيوعًا على Stack Overflow، وليس بدون سبب. by ليست مجرد سكر نحوي، بل هي آلية قوية للتفويض يمكنها تبسيط كودك بشكل جذري، وجعله أنظف، والتخلص من الكثير من الكود النمطي.
في هذه المقالة، سنقوم بتحليل by بالكامل. ستتعلم كيف يعمل تفويض الخصائص (property delegation) وتفويض الفئات (class delegation). سنجيب على الأسئلة الأكثر شيوعًا للمبتدئين ونعرض أمثلة حقيقية يمكنك تطبيقها فورًا في مشاريعك. هيا بنا!
ما هو التفويض في Kotlin؟ (الفكرة الرئيسية)
\n\nقبل الغوص في التفاصيل، دعنا نفهم الجوهر. التفويض هو نمط تصميم يقوم فيه الكائن بتفويض تنفيذ مهمته إلى كائن مساعد آخر. قامت Kotlin بدمج هذا النمط مباشرة في اللغة باستخدام الكلمة المفتاحية by. بدلاً من كتابة الكود «يدويًا»، تخبر المترجم: «مهلاً، دع هذا الشيء يفعل كل شيء نيابة عني».
يوجد نوعان رئيسيان من التفويض في Kotlin:
\n- \n
- تفويض الخصائص - عندما يتم تفويض getter/setter الخاصية إلى كائن آخر. \n
- تفويض الفئات - عندما يتم تفويض تنفيذ الواجهة إلى كائن آخر. \n
كلا الخيارين يستخدمان نفس الكلمة المفتاحية - by. دعنا نحلل كل منهما.
1. تفويض الخصائص (Property Delegation): by للحقول
\n\nهذا هو الاستخدام الأكثر شيوعًا لـ by. يسمح بإعادة استخدام منطق getter و setter للخصائص. بدلاً من كتابة كود متكرر في كل مرة، تستخدم مفوضات جاهزة أو خاصة بك.
مثال 1: by lazy - التهيئة الكسولة
\n\nتخيل أن لديك خاصية يتم إنشاؤها ببطء (على سبيل المثال، تحميل بيانات من الشبكة). لا تريد حسابها عند إنشاء الفئة، بل عند أول وصول إليها. by lazy يفعل ذلك بالضبط:
val heavyData: String by lazy {\n println("جاري حساب البيانات...")\n "نتيجة ثقيلة جداً"\n}\n\nfun main() {\n println("البداية")\n println(heavyData) // الاستدعاء الأول: نقوم بالحساب\n println(heavyData) // الاستدعاء الثاني: نأخذ من الذاكرة المؤقتة\n}\n\nالمخرجات:
\nالبداية\nجاري حساب البيانات...\nنتيجة ثقيلة جداً\nنتيجة ثقيلة جداً\n\nماذا حدث هنا؟ الكلمة المفتاحية by تقول: «الخاصية heavyData تفوض تنفيذها إلى الكائن lazy { ... }». هذا الكائن يضمن أن كتلة الكود سيتم تنفيذها مرة واحدة فقط، عند أول وصول، ثم يحفظ النتيجة. هذا مثالي للإعدادات، واتصالات قاعدة البيانات، وغيرها من الكائنات «الباهظة».
مثال 2: by observable - مراقبة التغييرات
\n\nهل تريد تنفيذ إجراء معين في كل مرة تتغير فيها الخاصية؟ استخدم Delegates.observable من المكتبة القياسية:
import kotlin.properties.Delegates\n\nvar watchedValue: Int by Delegates.observable(0) { property, oldValue, newValue ->\n println("القديم: $oldValue, الجديد: $newValue")\n}\n\nfun main() {\n watchedValue = 10 // القديم: 0, الجديد: 10\n watchedValue = 42 // القديم: 10, الجديد: 42\n}\n\nهذا النمط لا غنى عنه لـ البرمجة التفاعلية، والتحقق من صحة البيانات، أو مزامنة واجهة المستخدم. بالمناسبة، by هنا يعمل تمامًا مثل