استخدام لغة Kotlin للفرق الكبيرة

قد يكون الانتقال إلى أي لغة جديدة مهمة شاقة. لتحقيق النجاح، عليك البدء ببطء، والانتقال إلى مراحل أخرى، والاختبار بشكل متكرر لضمان توافق فريقك مع هذه المراحل. تسهّل لغة Kotlin عملية نقل البيانات، لأنّها تُجمَّع إلى رمز بايت JVM وتتوافق تمامًا مع Java.

بناء الفريق

تتمثّل الخطوة الأولى قبل نقل البيانات في بناء فهم أساسي مشترك لفريقك. في ما يلي بعض النصائح التي قد تفيدك في تسريع عملية التعلّم لدى فريقك.

تشكيل مجموعات دراسية

تُعدّ مجموعات الدراسة طريقة فعّالة لتسهيل التعلّم والاحتفاظ بالمعلومات. تشير الدراسات إلى أنّ تكرار ما تعلّمته في مجموعة يساعد في ترسيخ المعلومات. احصل على كتاب Kotlin أو مواد دراسية أخرى لكل فرد في المجموعة، واطلب من المجموعة قراءة فصلين كل أسبوع. خلال كل اجتماع، يجب أن تقارن المجموعة ما تعلّمته وتناقش أي أسئلة أو ملاحظات.

بناء ثقافة التعليم

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

تحديد بطل

أخيرًا، حدِّد شخصًا بارعًا لقيادة جهود التعلّم. يمكن لهذا الشخص أن يكون خبيرًا مختصًا (SME) عند بدء عملية التكيّف. من المهم تضمين هذا الشخص في جميع اجتماعات التدريب المتعلقة بلغة Kotlin. من المفترض أن يكون هذا الشخص مهتمًا بلغة Kotlin ولديه بعض المعرفة العملية بها.

الدمج ببطء

من المهم البدء ببطء والتفكير بشكل استراتيجي في الأجزاء التي يجب نقلها أولاً من منظومتك المتكاملة. من الأفضل غالبًا عزل هذه المشكلة في تطبيق واحد ضمن مؤسستك بدلاً من تطبيق رئيسي. وفي ما يتعلق بنقل التطبيق الذي تم اختياره، تختلف كل حالة عن الأخرى، ولكن إليك بعض الأماكن الشائعة التي يمكنك البدء منها.

نموذج البيانات

من المحتمل أن يتضمّن نموذج البيانات الكثير من معلومات الحالة بالإضافة إلى بعض الطرق. قد يحتوي نموذج البيانات أيضًا على طرق شائعة، مثل toString() وequals() وhashcode(). ويمكن عادةً نقل هذه الطرق واختبارها بشكل منفصل بسهولة.

على سبيل المثال، لنفترض أنّ لديك مقتطف Java التالي:

public class Person {

   private String firstName;
   private String lastName;
   // ...

   public String getFirstName() {
       return firstName;
   }

   public void setFirstName(String firstName) {
       this.firstName = firstName;
   }

   public String getLastName() {
       return lastName;
   }

   public void setLastName(String lastName) {
       this.lastName = lastName;
   }

   @Override
   public boolean equals(Object o) {
       if (this == o) return true;
       if (o == null || getClass() != o.getClass()) return false;
       Person person = (Person) o;
       return Objects.equals(firstName, person.firstName) &&
               Objects.equals(lastName, person.lastName);
   }

   @Override
   public int hashCode() {
       return Objects.hash(firstName, lastName);
   }

   @Override
   public String toString() {
       return "Person{" +
               "firstName='" + firstName + '\'' +
               ", lastName='" + lastName + '\'' +
               '}';
   }
}

يمكنك استبدال فئة Java بسطر واحد من Kotlin، كما هو موضّح هنا:

data class Person(var firstName: String?, var lastName: String?)

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

نقل الاختبارات

هناك مسار آخر يمكنك اتّباعه وهو تحويل الاختبارات الحالية وبدء كتابة اختبارات جديدة بلغة Kotlin. ويمكن أن يمنح ذلك فريقك الوقت الكافي للتعوّد على اللغة قبل كتابة الرمز الذي تخطّط لشحنه مع تطبيقك.

نقل طرق الأدوات المساعدة إلى دوال الإضافة

يمكن تمثيل أي فئات أدوات ثابتة (StringUtils وIntegerUtils وDateUtils وYourCustomTypeUtils وما إلى ذلك) على أنّها دوال إضافية في Kotlin ويمكن استخدامها من خلال قاعدة الرموز الحالية في Java.

على سبيل المثال، لنفترض أنّ لديك فئة StringUtils تتضمّن بعض الطرق:

package com.java.project;

public class StringUtils {

   public static String foo(String receiver) {
       return receiver...;  // Transform the receiver in some way
   }

   public static String bar(String receiver) {
       return receiver...;  // Transform the receiver in some way
   }

}

ويمكن بعد ذلك استخدام هذه الطرق في أماكن أخرى في تطبيقك، كما هو موضّح في المثال التالي:

...

String myString = ...
String fooString = StringUtils.foo(myString);

...

باستخدام دوال الإضافة في Kotlin، يمكنك توفير واجهة Utils نفسها للمتصلين بلغة Java، مع توفير واجهة برمجة تطبيقات أكثر اختصارًا لقاعدة رموز Kotlin المتزايدة في الوقت نفسه.

لإجراء ذلك، يمكنك البدء بتحويل فئة Utils هذه إلى Kotlin باستخدام عملية التحويل التلقائي التي توفّرها بيئة التطوير المتكاملة. قد تبدو نتيجة المثال مشابهة لما يلي:

package com.java.project

object StringUtils {

   fun foo(receiver: String): String {
       return receiver...;  // Transform the receiver in some way
   }

   fun bar(receiver: String): String {
       return receiver...;  // Transform the receiver in some way
   }

}

بعد ذلك، أزِل تعريف الفئة أو العنصر، وأضِف البادئة التي تشير إلى النوع الذي يجب تطبيق هذه الدالة عليه إلى كل اسم دالة، واستخدِم ذلك للإشارة إلى النوع داخل الدالة، كما هو موضّح في المثال التالي:

package com.java.project

fun String.foo(): String {
    return this...;  // Transform the receiver in some way
}

fun String.bar(): String {
    return this...;  // Transform the receiver in some way
}

أخيرًا، أضِف تعليقًا توضيحيًا JvmName إلى أعلى ملف المصدر لجعل الاسم الذي تم تجميعه متوافقًا مع بقية تطبيقك، كما هو موضّح في المثال التالي:

@file:JvmName("StringUtils")
package com.java.project
...

يجب أن تبدو النسخة النهائية مشابهة لما يلي:

@file:JvmName("StringUtils")
package com.java.project

fun String.foo(): String {
    return this...;  // Transform `this` string in some way
}

fun String.bar(): String {
    return this...;  // Transform `this` string in some way
}

يُرجى العِلم أنّه يمكن الآن استدعاء هذه الدوال باستخدام Java أو Kotlin مع اتّباع الاصطلاحات التي تتوافق مع كل لغة.

Kotlin

// ...

val myString: String = "hello"
val fooString = myString.foo()

// ...

Java

...
String myString = ...
String fooString = StringUtils.foo(myString);
...

إكمال عملية نقل البيانات

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

الاعتبارات

وكما أنّ Java لها أسلوبها الخاص، فإنّ Kotlin لها أسلوبها الخاص الذي يساهم في اختصارها. ومع ذلك، قد تجد في البداية أنّ رمز Kotlin الذي ينتجه فريقك يشبه إلى حد كبير رمز Java الذي يحلّ محلّه. يتغيّر ذلك بمرور الوقت مع زيادة خبرة فريقك في Kotlin. تذكَّر أنّ التغيير التدريجي هو مفتاح النجاح.

في ما يلي بعض الإجراءات التي يمكنك اتّخاذها لتحقيق التناسق مع زيادة حجم قاعدة رموز Kotlin البرمجية:

معايير الترميز الشائعة

احرص على تحديد مجموعة عادية من اصطلاحات الترميز في وقت مبكر من عملية اعتمادك. يمكنك الخروج عن دليل أسلوب Kotlin لنظام Android عندما يكون ذلك منطقيًا.

أدوات التحليل الثابت

يمكنك فرض معايير الترميز المحدّدة لفريقك باستخدام Android lint وأدوات التحليل الثابت الأخرى. يوفّر klint، وهو أداة تدقيق لغوي تابعة لجهة خارجية خاصة بلغة Kotlin، قواعد إضافية للغة Kotlin.

التكامل المستمر

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

إمكانية التشغيل التفاعلي

تتكامل لغة Kotlin مع Java بسلاسة في معظم الحالات، ولكن يُرجى ملاحظة ما يلي.

إمكانية قبول القيم الفارغة

تعتمد Kotlin على التعليقات التوضيحية عن إمكانية قبول القيم الفارغة في الرموز البرمجية المترجَمة لاستنتاج إمكانية قبول القيم الفارغة في Kotlin. في حال عدم توفير التعليقات التوضيحية، يتم تلقائيًا ضبط Kotlin على نوع النظام الأساسي الذي يمكن التعامل معه كنوع قابل أو غير قابل للتصرف. وقد يؤدي ذلك إلى حدوث مشاكل في وقت التشغيل NullPointerException، إذا لم يتم التعامل معه بعناية.

استخدام الميزات الجديدة

توفّر Kotlin الكثير من المكتبات الجديدة وميزات تسهّل كتابة الرموز البرمجية لتقليل الرموز المتكررة، ما يساعد في زيادة سرعة التطوير. مع ذلك، يجب توخّي الحذر واتّباع أسلوب منهجي عند استخدام دوال مكتبة Kotlin العادية، مثل دوال المجموعات والروتينات المشتركة وتعبيرات lambda.

في ما يلي خطأ شائع جدًا يقع فيه مطوّرو Kotlin الجدد. لنفترض أنّ لديك رمز Kotlin التالي:

val nullableFoo: Foo? = Foo()

// This lambda executes only if nullableFoo is not null
// and `foo` is of the non-nullable Foo type
nullableFoo?.let { foo ->
    foo.baz()
    foo.zap()
}

الغرض من هذا المثال هو تنفيذ foo.baz() وfoo.zap() إذا لم تكن قيمة nullableFoo فارغة، وبالتالي تجنُّب NullPointerException. على الرغم من أنّ هذا الرمز البرمجي يعمل على النحو المتوقّع، إلا أنّه أقل سهولة في القراءة من عملية التحقّق البسيطة من القيمة الفارغة والتحويل الذكي، كما هو موضّح في المثال التالي:

val nullableFoo: Foo? = null
if (nullableFoo != null) {
    nullableFoo.baz() // Using !! or ?. isn't required; the Kotlin compiler infers non-nullability
    nullableFoo.zap() // from guard condition; smart casts nullableFoo to Foo inside this block
}

الاختبار

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

يشكّل هذا السلوك مشكلة لأُطر المحاكاة، مثل Mockito، التي تعتمد على تنفيذ الواجهة أو الوراثة لتجاوز السلوكيات أثناء الاختبار. بالنسبة إلى اختبارات الوحدات، يمكنك تفعيل ميزة Mock Maker Inline في Mockito، ما يتيح لك إنشاء عناصر وهمية للفئات والأساليب النهائية. بدلاً من ذلك، يمكنك استخدام مكوّن All-Open الإضافي للمترجم لفتح أي فئة Kotlin وأعضائها التي تريد اختبارها كجزء من عملية الترجمة. تتمثّل الميزة الأساسية لاستخدام هذه الإضافة في أنّها تعمل مع كلّ من الاختبارات الموحّدة واختبارات قياس حالة التطبيق.

مزيد من المعلومات

لمزيد من المعلومات حول استخدام Kotlin، يُرجى الاطّلاع على الروابط التالية: