تعلّم البرمجة بلغة كوتلن (34): وظائف إضافية Extension Functions

استمع إلى المقال
|
درسنا في الأقسام السابقة من هذه الدورة، مواضيع عامة في برمجة وخوارزميات الحاسب، كمدخل للبرمجة عمومًا. ثم جهزنا بيئة التطوير، لنبدأ بعدها في دراسة أساسيات مهمة في البرمجة عبر كوتلن. وفي القسم السابق، استعرضنا مفهوم البرمجة الكائنية وتطبيقاتها في لغة كوتلن.
لا تختلف لغات برمجة الكمبيوتر كثيرًا في ما تجعله ممكنًا، ولكن فيما تجعله سهلاً.
لاري وول, مخترع لغة بيرل Perl
في هذا القسم، ندرس المزيد من المفاهيم التي طبقتها كوتلن في سهولة أو قابلية الإستخدام Usability، بجعل الممارسة الجيدة في كتابة الشفرات أسهل، والممارسة السيئة أصعب. تعلّم هذه المفاهيم، لا يجعل من المبرمجين أكثر كفاءة وسريعي الإنتاج للبرامج فحسب، بل يجعلهم ينتجونها بأقل قدر ممكن من الأخطاء. ونبدأ بأول درس، الوظائف الإضافية Extension Functions.
لتوفير الوقت، في الغالب لا يكتب المبرمجون كل شيء من البداية، ولكنهم يستخدمون شفرات مكتوبة بالفعل، والتي قد تأتي في شكل مكتبات. ولكن هذه الطريقة بها بعض الجوانب السلبية مثل عدم جدوى تعديل شفرة المكتبة، ﻷن مطورها قد يحدّثها بالتالي ستضيع التعديلات التي قمنا بها عليها. إذًا، كيف يمكننا تمديد وظائف الأصناف في المكتبة، بإضافة دوال تبقى حتى بعد تحديثها؟
حسنًا، توفر كوتلن القدرة على إنشاء دوال ملحقة، تعمل على توسيع أو تمديد وظائف الصنف أو حتى أنواع البيانات الأساسية في كوتلن، بوظائف جديدة، دون المساس أو تعديل السلوكيات الحالية للصنف أو نوع البيانات. ويمكن استدعاء هذه الدوال بالطريقة المعتادة، كما لو كانت دوالًا يملكها الصنف الأصلي Member Functions، الذي نريد توسيع قدرته ومهامه.
للإعلان عن دالة ملحقة، نبدأ بإسم الصنف أو نوع البيانات المستقبل لهذه الدالة والمراد تمديد وظائفه receiver type، ثم نقطة (.)، ثم اسم الدالة:
fun ReceiverType.extensionFunction() { … }
بدأنا بكلمة fun، ﻷنها دالة عادية، مع إختلاف أنها تبدأ باسم الصنف أو نوع البيانات ReceiverType. لأننا نريد منها أن تضيف وظيفة لهذا الصنف أو نوع البيانات.
إذا افترضنا أنه لدينا نص معين، ونريد أن نطبع الحرف الأول والأخير في هذا النص، يمكننا فعل ذلك باستخدام دالتي ()first و ()last من مكتبة كوتلن القياسية:
عند تنفيذ الشفرة، سيكون الناتج:
ولكن، بالإضافة لطباعة الحرفين الأول والأخير، نحن أيضًا نريد طباعة الحرف الأوسط في النص. لذا بحثنا ما إذا كان الصنف String يحتوي على دالة باسم ()middle بنفس طريقة الدالتين ()first و ()last:
كما نرى في الصورة، أنه لاتوجد دالة بهذا الاسم في الصنف String، لذا تظهر باللون الأحمر في برنامج IntelliJ. وحتى عند تجاهل الخطأ وتنفيذ البرنامج، لن يتم ترجمة الشفرة، وسينتج الخطأ:
Unresolved reference: middle
وهذا ﻷن مترجم كوتلن لم يجد دالة باسم ()middle، مرتبطة بالصنف String. إذًا، ما هو الحل؟
الحل بكل بساطة، هو إلحاق دالة بالصنف String، تكون وظيفتها إعادة الحرف الأوسط في أي كائن من النوع String في برنامجنا.
لأن نوع البيانات الأساسي String هو من الأصناف التي تأتي جاهزة مع لغة كوتلن ولا يمكننا التعديل عليها، يمكننا إضافة وظيفة إليه فقط عبر ميزة Extension Function التي توفرها كوتلن:
أنشأنا دالة باستخدام الكلمة المفتاحية fun، وأسميناها middle، وجعلنا الدالة تعيد النوع Char. ولأن النوع String هو عبارة عن تجميعة من المحارف Char، يمكننا التعامل معه مثل قائمة List غير قابلة للتغيير. بالتالي، للوصول إلى أي عنصر (محرف) في النص String، يمكننا استخدام الفهرس الذي يُمثله.
لذلك، داخل الأقواس المعقوفة للدالة، أعلنا عن متغير middleCharIndex من النوع Int، ليُمثل فهرس الحرف الأوسط في النص String. والتعبير الذي أسندناه للمتغير middleCharIndex، هو عدد محارف النص، الذي استخدمنا الكلمة المفتاحية this والخاصية length من الصنف String لإيجاده، ثم قسمنا الناتج على الرقم 2.
فمثلًا، إذا كان عدد محارف النص 5 محارف، 5 / 2 = 2، بالتالي ستكون قيمة المتغير middleCharIndex تساوي 2. وكنتيجة، ستعيد الدالة العنصر الذي فهرسه 2:
return this[2]
بالطبع 5 تقسيم 2 يساوي 2.5، ولكن كما قلنا في درس أنواع العدد Number Types، أنه عند تقسيم عدد صحيح Int على عدد صحيح آخر، يتم تجاهل الفاصلة العشرية وما بعدها، لأن هذه هي الطريقة القياسية عند التعامل مع الأعداد الصحيحة من نوع البيانات Int.
ولأنه لايوجد فهرس بكسور عشرية، كان استخدامنا لمتغير من النوع Int مناسب جداً في حالتنا هذه، لأنه سيتم التخلي عن الفاصلة العشرية وما بعدها من أرقام عند التقسيم. ثم جعلنا الدالة تعيد الحرف الذي يُمثله رقم الفهرس الناتج من التقسيم، باستخدام الكلمة this مرة أخرى. إذًا، ماذا تعني الكلمة المفتاحية this؟
هي كلمة تشير إلى الكائن الذي تم استدعاء الدالة الملحقة بواسطته باستخدام النقطة (.). فمثلًا، في مثالنا أعلاه، تشير الكلمة this إلي الكائن النصي “ExVar”، والذي يُمثله المتغير platform. فعند كتابة:
platform.middle()
سيتم تعويض الكائن platform في الدالة ()middle. وهذا يعني كأننا كتبنا:
بالطبع هذه الشفرة للشرح فقط ولا يمكننا كتابة المتغير platform مباشرة في دالة ()middle، لأنه خاص بالدالة ()main فقط، ولا تعرف أية دالة غيرها بوجوده. لذا نستعيض عن المتغير platform بالكلمة المفتاحية this، في دالة ()middle المضافة للصنف String.
يمكننا تعديل الشفرة وإيجاد الحرف الأوسط للكلمة “ExVar” باستخدام دالتنا المضافة للصنف String:
وعند تنفيذ الشفرة مرة أخرى، ستكون النتيجة:
ويفضل دائماً اختصار الشفرات في كوتلن، إذا كانت ستزيد من قابلية القراءة. وفي دالة ()middle، يمكننا فعل التالي:
fun String.middle() = this[this.length / 2]
ما زالت الدالة مقروءة، لأن التعبير this.length / 2 ينتج رقم معين، وهو الذي سيتم استخدامه كفهرس لإيجاد المحرف في السلسلة النصية التي يُمثلها الكائن this. تخلينا أيضًا عن نوع الإرجاع Char للدالة، لأن كوتلن ستتعرف عليه من التعبير يمين علامة الإسناد =.
دعونا نفترض أنه لدينا الصنف التالي:
الصنف A يحتوي على دالة عضو اسمها member. ثم لاحقًا كتبنا له دالة ملحقة اسمها extension. كلتا الدالتين يمكن استدعائهما بنفس الطريقة:
val a = A()
a.member()
a.extension()
أو
A().member()
A().extension()
نلاحظ أنه لا يوجد فرق عند الاستدعاء. لذا يكون من الصعب التفريق بين الدالتين إذا لم ننظر إلى الشفرة الداخلية للصنف.
أما إذا كتبنا دالة ملحقة بنفس اسم الدالة العضو، كأن ننشئ دالة ملحقة اسمها member، سيتم تجاهل الدالة الملحقة واستدعاء الدالة العضو. كما يظهر في الصورة التالية:
إذا كنا فعليًا نحتاج إلى إنشاء دالة ملحقة بنفس اسم الدالة العضو، يجب أن نغير توقيعها. كأن نضع لها معاملات parameters مختلفة عن تلك الموجودة بالدالة العضو. وهو ما يعرف بالـ Overloading، والذي سنشرحه تفصيليًا في الدرس القادم.
لمزيد من التدرب على الدوال الملحقة وغيرها مما درسناه حتى الآن في هذه الدورة، دعونا نقوم بكتابة برنامج يطلب من المستخدم ادخال عدد غير محدود من الأعداد الصحيحة. ثم يقوم البرنامج بفرز هذه الأعداد والإبقاء على الأعداد المزدوجة فقط، ويطبعها:
الشرح الكامل للشفرة في الصورة التالية:
عند تنفيذ هذه الشفرة، سيكون الناتج:
كما نرى في الصورة، تم تجاهل الأعداد الفردية وطباعة فقط الأعداد المزدوجة.
ملحوظة: نلاحظ أن الأرقام التي أدخلها المستخدم للبرنامج، والتي تظهر باللون الأخضر في الصورة أعلاه، هي نفس أرقام الدروس في دورة كوتلن، والتي يجب مراجعتها لفهم هذه الشفرة.
وفي النهاية، تعد إضافة وظائف للأصناف، أداة مفيدة يمكن أن تساعدنا في العمل مع الأصناف التي تم إنشاؤها مسبقًا. لأنه في بعض الأحيان لا يمكننا تعديل شفرة هذه الأصناف. أو قد نحتاج إلى إضافة بعض الوظائف لصنف يخصنا، ولكن لا نريد كتابتها فيه، لتجنب طول شفرة الصنف مثلًا. لكل ذلك، تعد الدوال الملحقة Extension Functions، هي الحل الأمثل.
هذا الدرس هو جزء من سلسلة تعليم مبادئ البرمجة بلغة كوتلن. لمُتابعة الدروس منذ البداية ومُشاهدة فهرس المحتويات يمكنك الانتقال إلى الدرس الأول من هنا.
هل أعجبك المحتوى وتريد المزيد منه يصل إلى صندوق بريدك الإلكتروني بشكلٍ دوري؟
انضم إلى قائمة من يقدّرون محتوى إكسڤار واشترك بنشرتنا البريدية.