ใช้ Kotlin สำหรับทีมขนาดใหญ่

การย้ายไปใช้ภาษาใหม่เป็นเรื่องที่ท้าทาย หากต้องการเตรียมทีมให้พร้อมรับความสำเร็จ ให้เริ่มอย่างช้าๆ ย้ายข้อมูลทีละส่วนเล็กๆ และทดสอบบ่อยๆ Kotlin จะคอมไพล์เป็น JVM bytecode และทำงานร่วมกับ Java ได้อย่างเต็มรูปแบบ ซึ่งทำให้การย้ายข้อมูลง่ายขึ้น

การสร้างทีม

ขั้นตอนแรกก่อนย้ายข้อมูลคือการสร้างความเข้าใจพื้นฐานร่วมกันสำหรับทีม เคล็ดลับต่อไปนี้อาจเป็นประโยชน์ในการเร่งการเรียนรู้ของทีม

จัดตั้งกลุ่มศึกษา

กลุ่มศึกษาเป็นวิธีที่มีประสิทธิภาพในการอำนวยความสะดวกในการเรียนรู้และการจดจำ การศึกษาชี้ให้เห็น ว่าการท่องสิ่งที่เรียนรู้ในสภาพแวดล้อมแบบกลุ่มช่วย เสริมเนื้อหา ซื้อหนังสือ Kotlin หรือ สื่อการเรียนรู้อื่นๆ ให้สมาชิกแต่ละคนในกลุ่ม และขอให้กลุ่มอ่านหนังสือ 2-3 บทในแต่ละสัปดาห์ ในระหว่างการพบปะกันแต่ละครั้ง กลุ่มควรเปรียบเทียบสิ่งที่ได้เรียนรู้และพูดคุยเกี่ยวกับคำถามหรือข้อสังเกตต่างๆ

สร้างวัฒนธรรมการสอน

แม้ว่าไม่ใช่ทุกคนจะคิดว่าตัวเองเป็นครู แต่ทุกคนก็สอนได้ ตั้งแต่หัวหน้าทีมหรือหัวหน้าฝ่ายเทคโนโลยีไปจนถึงผู้ร่วมงานแต่ละคน ทุกคนสามารถส่งเสริมสภาพแวดล้อมการเรียนรู้ที่จะช่วยให้มั่นใจได้ถึงความสำเร็จ วิธีหนึ่งในการอำนวยความสะดวกในเรื่องนี้คือการจัดงานนำเสนอเป็นระยะๆ โดยกำหนดให้บุคคลหนึ่งในทีมพูดถึงสิ่งที่ได้เรียนรู้หรือต้องการแชร์ คุณสามารถใช้ประโยชน์จากกลุ่มศึกษาได้โดยขออาสาสมัครให้นำเสนอบทใหม่ในแต่ละสัปดาห์จนกว่าทีมจะรู้สึกคุ้นเคยกับภาษา

กำหนดแชมเปียน

สุดท้าย ให้กำหนดแชมเปียนเพื่อเป็นผู้นำความพยายามในการเรียนรู้ บุคคลนี้สามารถทำหน้าที่เป็น ผู้เชี่ยวชาญเฉพาะเรื่อง (SME) เมื่อคุณเริ่มกระบวนการนำไปใช้ คุณควรเชิญบุคคลนี้เข้าร่วมการประชุมฝึกซ้อมทั้งหมดที่เกี่ยวข้องกับ Kotlin บุคคลนี้ควรมีความกระตือรือร้นเกี่ยวกับ Kotlin อยู่แล้วและมีความรู้ในการทำงานบ้าง

ผสานรวมอย่างช้าๆ

การเริ่มต้นอย่างช้าๆ และคิดอย่างมีกลยุทธ์เกี่ยวกับส่วนใดของระบบนิเวศที่จะย้ายก่อนเป็นสิ่งสำคัญ โดยส่วนใหญ่มักจะดีที่สุดหากจำกัดการย้ายข้อมูลไว้ที่แอปเดียวภายในองค์กรแทนที่จะเป็นแอปหลัก ในส่วนของการย้ายข้อมูลแอปที่เลือกนั้น แต่ละสถานการณ์จะแตกต่างกัน แต่ต่อไปนี้คือจุดเริ่มต้นทั่วไปบางส่วน

โมเดลข้อมูล

โมเดลข้อมูลของคุณน่าจะประกอบด้วยข้อมูลสถานะจำนวนมากพร้อมกับเมธอด 2-3 รายการ โมเดลข้อมูลอาจมีเมธอดทั่วไป เช่น 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?)

จากนั้นคุณสามารถทำ Unit Test โค้ดนี้กับชุดโปรแกรมทดสอบปัจจุบันได้ เริ่มจากโมเดลเดียวในแต่ละครั้ง และย้ายข้อมูลคลาสที่เป็นสถานะส่วนใหญ่แทนที่จะเป็นลักษณะการทำงาน และทดสอบบ่อยๆ

ย้ายข้อมูลการทดสอบ

อีกจุดเริ่มต้นหนึ่งคือการทดสอบ โดยแปลงการทดสอบที่มีอยู่และเขียนการทดสอบใหม่ใน Kotlin วิธีนี้จะช่วยให้ทีมมีเวลาทำความคุ้นเคยกับภาษาก่อนที่คุณจะเขียนโค้ดที่เผยแพร่พร้อมกับแอป

ย้ายข้อมูลเมธอดของยูทิลิตีไปยังฟังก์ชันส่วนขยาย

คุณสามารถแสดงคลาสยูทิลิตีแบบคงที่ เช่น StringUtils, IntegerUtils, DateUtils หรือ YourCustomTypeUtils เป็น ฟังก์ชันส่วนขยายของ Kotlin และใช้ฟังก์ชันเหล่านั้นจากฐานโค้ด Java ที่มีอยู่

ตัวอย่างเช่น สมมติว่าคุณมีคลาส StringUtils ที่มีเมธอด 2-3 รายการดังนี้

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 พร้อมทั้งมี API ที่กระชับมากขึ้นสำหรับฐานโค้ด 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 แล้วและคุณได้ย้ายข้อมูลส่วนเล็กๆ แล้ว คุณสามารถย้ายไปใช้คอมโพเนนต์ขนาดใหญ่ขึ้น เช่น Fragment, Activity, ออบเจ็กต์ ViewModel และคลาสอื่นๆ ที่เกี่ยวข้องกับตรรกะทางธุรกิจ

ข้อควรพิจารณา

เช่นเดียวกับที่ Java มีสไตล์เฉพาะ Kotlin ก็มีสไตล์เฉพาะของตัวเองซึ่งช่วยให้ภาษาดูกระชับ อย่างไรก็ตาม ในตอนแรกคุณอาจพบว่าโค้ด Kotlin ที่ทีมเขียนมีลักษณะคล้ายกับโค้ด Java ที่กำลังจะแทนที่ ซึ่งจะเปลี่ยนแปลงไปเมื่อทีมได้รับประสบการณ์การใช้งาน Kotlin มากขึ้น การเปลี่ยนแปลงแบบค่อยเป็นค่อยไปคือกุญแจสู่ความสำเร็จ

ต่อไปนี้คือสิ่งที่คุณทำได้เพื่อรักษาความสอดคล้องของฐานโค้ด Kotlin เมื่อฐานโค้ดเติบโตขึ้น

มาตรฐานการเขียนโค้ดทั่วไป

กำหนดชุดมาตรฐานการเขียนโค้ดตั้งแต่เนิ่นๆ ในกระบวนการนำไปใช้ คุณสามารถเบี่ยงเบนจากคู่มือสไตล์ Kotlin ของ Android ได้หากสมเหตุสมผล

เครื่องมือวิเคราะห์แบบคงที่

บังคับใช้ชุดมาตรฐานการเขียนโค้ดที่กำหนดไว้สำหรับทีมโดยใช้ Android Lint และเครื่องมือวิเคราะห์แบบคงที่อื่นๆ ktlint ซึ่งเป็น Linter ของ Kotlin จากบุคคลที่สาม ยังมีกฎเพิ่มเติมสำหรับ Kotlin ด้วย

การรวมอย่างต่อเนื่อง

ตรวจสอบว่าคุณปฏิบัติตามมาตรฐานการเขียนโค้ดทั่วไปและมีการทดสอบโค้ด Kotlin อย่างครอบคลุม การทำให้การทดสอบเป็นส่วนหนึ่งของกระบวนการสร้างอัตโนมัติจะช่วยให้มั่นใจได้ถึงความสอดคล้องและการปฏิบัติตามมาตรฐานเหล่านี้

ความสามารถในการทำงานร่วมกัน

Kotlin ทำงานร่วมกับ Java ได้อย่างราบรื่นในกรณีส่วนใหญ่ แต่โปรดคำนึงถึงประเด็นต่อไปนี้

ความสามารถในการเว้นว่าง

Kotlin อาศัยคำอธิบายประกอบความสามารถในการเว้นว่างในโค้ดที่คอมไพล์แล้วเพื่ออนุมานความสามารถในการเว้นว่างในฝั่ง Kotlin หากไม่มีคำอธิบายประกอบ Kotlin จะใช้ประเภทแพลตฟอร์มเริ่มต้นที่ถือได้ว่าเป็นประเภทที่เว้นว่างได้หรือไม่เว้นว่างก็ได้ หากคุณไม่จัดการประเภทแพลตฟอร์มอย่างระมัดระวัง อาจทำให้เกิดปัญหา NullPointerException ขณะรันไทม์

นำฟีเจอร์ใหม่ไปใช้

Kotlin มีไลบรารีใหม่ๆ และไวยากรณ์ที่กระชับมากมายเพื่อลดโค้ดซ้ำ ซึ่งช่วยเพิ่มความเร็วในการพัฒนา อย่างไรก็ตาม โปรดระมัดระวังและเป็นระบบเมื่อใช้ฟังก์ชันไลบรารีมาตรฐานของ Kotlin เช่น ฟังก์ชันคอลเล็กชัน, Coroutine และ 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 ไม่เป็น Null เพื่อหลีกเลี่ยง NullPointerException แม้ว่าโค้ดนี้จะทำงานได้ตามที่คาดไว้ แต่ก็อ่านได้ยากกว่าการตรวจสอบ Null และ การแคสต์อัจฉริยะดังที่แสดงในตัวอย่างต่อไปนี้

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 ซึ่งอาศัยการติดตั้งใช้งานอินเทอร์เฟซหรือการสืบทอดเพื่อลบล้างลักษณะการทำงานระหว่างการทดสอบ สำหรับการทำ Unit Test คุณสามารถเปิดใช้ฟีเจอร์ Mock Maker Inline ของ Mockito ซึ่งช่วยให้คุณจำลองคลาสและเมธอดสุดท้ายได้ หรือคุณจะใช้ ปลั๊กอินคอมไพเลอร์ All-Open เพื่อเปิดคลาส Kotlin และสมาชิกของคลาสที่ต้องการทดสอบเป็นส่วนหนึ่งของ กระบวนการคอมไพล์ก็ได้ ข้อได้เปรียบหลักของการใช้ปลั๊กอินนี้คือปลั๊กอินทำงานได้กับการทดสอบหน่วยและการทดสอบการวัดคุม

ข้อมูลเพิ่มเติม

ดูข้อมูลเพิ่มเติมเกี่ยวกับการใช้ Kotlin ได้ที่ลิงก์ต่อไปนี้