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

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

بناء الفريق

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

إنشاء مجموعات للدراسة

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

بناء ثقافة التدريس

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

تحديد بطل

وأخيرًا، عيّن بطلاً لقيادة جهود التعلم. يمكن لهذا الشخص أن يعمل كخبير مختص (SME) عند بدء عملية الاعتماد. من المهم إشراك هذا الشخص في جميع اجتماعات التدريب المتعلقة بكوتلين. من الناحية المثالية، هذا الشخص شغوف بالفعل بلغة 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 = ...
val fooString = myString.foo()
...

Java

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

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

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

الاعتبارات

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

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

معايير البرمجة الشائعة

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

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

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

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

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

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

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

قابلية إلغاء

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

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

وتوفر لغة Kotlin الكثير من المكتبات الجديدة والسكر التوليدي للتخفيف من النصوص النموذجية، مما يساعد على زيادة سرعة التطوير. ومع ذلك، كن حذرًا ومنهجيًا عند استخدام دوال مكتبة Kotlin القياسية، مثل دوال التجميع والكوروتينات وlambdas.

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

val nullableFoo: 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، يمكنك الاطلاع على الروابط التالية: