استخدام لغة 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 باستخدام التحويل التلقائي الذي توفّره بيئة التطوير المتكاملة (IDE). قد يبدو الناتج المشابه للمثال التالي:

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 وأدوات التحليل الثابت الأخرى. توفّر أداة ktlint، وهي أداة تدقيق 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 هذا الخيار لتعزيز التركيب بدلاً من الوراثة. للمساعدة في تبسيط التركيب، تتوافق Kotlin تلقائيًا مع تنفيذ السلوك من خلال التفويض.

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

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

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