Wdrożenie Kotlin w dużych zespołach

Przejście na nowy język może być trudne. Aby Twój zespół odniósł sukces, zacznij powoli, przeprowadzaj migrację w małych partiach i często testuj. Kotlin jest kompilowany do kodu bajtowego JVM i jest w pełni interoperacyjny z Javą, co ułatwia migrację.

Budowanie zespołu

Pierwszym krokiem przed migracją jest zbudowanie wspólnego zrozumienia podstaw przez zespół. Oto kilka wskazówek, które mogą pomóc w przyspieszeniu nauki zespołu.

Tworzenie grup naukowych

Grupy naukowe to skuteczny sposób na ułatwienie nauki i zapamiętywania. Badania sugerują że powtarzanie tego, czego się nauczyłeś, w grupie pomaga utrwalić materiał. Kup książkę o Kotlinie lub inne materiały do nauki dla każdego członka grupy i poproś grupę o przeczytanie kilku rozdziałów tygodniowo. Podczas każdego spotkania grupa powinna porównać to, czego się nauczyła, i omówić wszelkie pytania lub spostrzeżenia.

Budowanie kultury nauczania

Nie każdy uważa się za nauczyciela, ale każdy może uczyć. Od kierownika zespołu lub technologii po indywidualnego współpracownika – każdy może zachęcać do tworzenia środowiska uczenia się, które może pomóc w osiągnięciu sukcesu. Jednym ze sposobów na ułatwienie tego jest organizowanie okresowych prezentacji, podczas których jedna osoba z zespołu jest wyznaczona do opowiedzenia o czymś, czego się nauczyła lub czym chce się podzielić. Możesz wykorzystać grupę naukową, prosząc ochotników o prezentowanie nowego rozdziału co tydzień, aż zespół poczuje się swobodnie w tym języku.

Wyznaczanie mistrza

Na koniec wyznacz mistrza, który będzie kierować procesem uczenia się. Ta osoba może pełnić rolę eksperta w danej dziedzinie (SME) podczas rozpoczynania procesu wdrażania. Ważne jest, aby ta osoba uczestniczyła we wszystkich spotkaniach dotyczących Kotlina. Najlepiej, jeśli ta osoba pasjonuje się już Kotlinem i ma pewną wiedzę praktyczną.

Powolna integracja

Kluczem jest powolne rozpoczynanie i strategiczne myślenie o tym, które części ekosystemu należy przenieść w pierwszej kolejności. Często najlepiej jest odizolować to do jednej aplikacji w organizacji, a nie do flagowej aplikacji. Jeśli chodzi o migrację wybranej aplikacji, każda sytuacja jest inna, ale oto kilka typowych miejsc, od których można zacząć.

Model danych

Model danych prawdopodobnie składa się z wielu informacji o stanie i kilku metod. Model danych może też zawierać typowe metody, takie jak toString, equals i hashCode. Zwykle możesz przeprowadzić migrację tych metod i przetestować je w izolacji.

Załóżmy na przykład, że masz ten fragment kodu w Javie:

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 + '\'' +
               '}';
   }
}

Klasę Java możesz zastąpić jednym wierszem kodu w Kotlinie, jak pokazano w tym przykładzie:

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

Następnie możesz przetestować ten kod w ramach bieżącego zestawu testów jednostkowych. Zacznij od małych kroków, po jednym modelu, i przeprowadź migrację klas, które są głównie stanem, a nie zachowaniem. Po drodze często testuj.

Migracja testów

Innym miejscem, od którego możesz zacząć, są testy: przekonwertuj istniejące testy i napisz nowe testy w Kotlinie. Dzięki temu Twój zespół będzie miał czas na zapoznanie się z językiem, zanim napiszesz kod, który będzie dostarczany z aplikacją.

Przenoszenie metod narzędziowych do funkcji rozszerzeń

Statyczne klasy narzędziowe, takie jak StringUtils, IntegerUtils, DateUtils, czy YourCustomTypeUtils, możesz przedstawić jako funkcje rozszerzeń Kotlina i używać ich w istniejącej bazie kodu Java.

Załóżmy na przykład, że masz klasę StringUtils z kilkoma metodami:

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
   }

}

Te metody mogą być używane w innych miejscach aplikacji, jak pokazano w tym przykładzie:

...

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

...

Korzystając z funkcji rozszerzeń Kotlina, możesz udostępnić ten sam interfejs Utils wywołującym w Javie, a jednocześnie zaoferować bardziej zwięzłe API dla rosnącej bazy kodu Kotlin.

Aby to zrobić, możesz zacząć od przekonwertowania tej klasy Utils na Kotlin za pomocą automatycznej konwersji udostępnianej przez IDE. Przykładowe dane wyjściowe mogą wyglądać tak:

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
   }

}

Następnie usuń definicję klasy lub obiektu, dodaj do każdej nazwy funkcji typ, do którego ta funkcja powinna się odnosić, i użyj go do odwoływania się do typu w funkcji, jak pokazano w tym przykładzie:

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
}

Na koniec dodaj adnotację JvmName na początku pliku źródłowego, aby skompilowana nazwa była zgodna z resztą aplikacji, jak pokazano w tym przykładzie:

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

Ostateczna wersja powinna wyglądać tak:

@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
}

Pamiętaj, że te funkcje można teraz wywoływać w Javie lub Kotlinie zgodnie z konwencjami każdego języka.

Kotlin

// ...

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

// ...

Java

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

Zakończenie migracji

Gdy Twój zespół będzie już dobrze znał Kotlin i przeprowadzisz migrację mniejszych obszarów, możesz przejść do większych komponentów, takich jak fragmenty, aktywności, obiekty ViewModel i inne klasy związane z logiką biznesową.

Uwagi

Podobnie jak Java ma określony styl, Kotlin ma swój własny idiomatyczny styl, który przyczynia się do jego zwięzłości. Na początku może się jednak okazać, że kod Kotlin pisany przez Twój zespół bardziej przypomina kod Java, który zastępuje. Zmieni się to, gdy Twój zespół zdobędzie doświadczenie w Kotlinie. Kluczem do sukcesu jest stopniowa zmiana.

Oto kilka rzeczy, które możesz zrobić, aby baza kodu Kotlin była spójna w miarę jej rozrastania się:

Typowe standardy kodowania

Na wczesnym etapie procesu wdrażania zdefiniuj zestaw standardów konwencji kodowania. W uzasadnionych przypadkach możesz odbiegać od przewodnika po stylu Kotlin na Androidzie .

Narzędzia do analizy statycznej

Wymuszaj przestrzeganie zestawu standardów kodowania ustalonych dla zespołu, używając narzędzia Android lint i innych narzędzi do analizy statycznej. ktlint, narzędzie do sprawdzania kodu Kotlin firmy zewnętrznej, udostępnia też dodatkowe reguły dla Kotlina.

Tryb ciągłej integracji

Pamiętaj, aby przestrzegać typowych standardów kodowania i zapewnić wystarczający zasięg testów kodu Kotlin. Włączenie tego do zautomatyzowanego procesu kompilacji może pomóc w zapewnieniu spójności i przestrzegania tych standardów.

Interoperacyjność

Kotlin w większości przypadków bezproblemowo współpracuje z Javą, ale pamiętaj o tych kwestiach.

Dopuszczalność wartości null

Kotlin opiera się na adnotacjach dopuszczalności wartości null w skompilowanym kodzie, aby określić dopuszczalność wartości null po stronie Kotlina. Jeśli adnotacje nie są podane, Kotlin domyślnie używa typu platformy, który można traktować jako dopuszczający lub niedopuszczający wartości null. Jeśli nie będziesz ostrożnie obsługiwać typów platformy, może to prowadzić do problemów z NullPointerException w czasie działania.

Wdrażanie nowych funkcji

Kotlin udostępnia wiele nowych bibliotek i cukru syntaktycznego, które zmniejszają ilość kodu szablonowego, co pomaga zwiększyć szybkość programowania. Należy jednak zachować ostrożność i metodyczność podczas korzystania z funkcji biblioteki standardowej Kotlina, takich jak funkcje kolekcji, współprogramy, i wyrażenia lambda.

Oto typowa pułapka, w którą wpadają deweloperzy, którzy dopiero zaczynają pracę z Kotlinem. Rozważmy ten kod 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()
}

W tym przykładzie chodzi o wykonanie foo.baz i foo.zap, jeśli nullableFoo nie jest wartością null, co pozwala uniknąć NullPointerException. Ten kod działa zgodnie z oczekiwaniami, ale jest mniej intuicyjny niż sprawdzanie wartości null i inteligentne rzutowanie, jak pokazano w tym przykładzie:

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
}

Testowanie

W Kotlinie klasy i ich funkcje są domyślnie zamknięte na rozszerzenie. Musisz jawnie otworzyć klasy i funkcje, które chcesz podklasować. Kotlin podejmuje tę decyzję, aby promować kompozycję zamiast dziedziczenia. Aby uprościć kompozycję, Kotlin ma wbudowaną obsługę implementowania zachowania przez delegowanie.

To zachowanie stanowi problem dla platform do tworzenia atrap, takich jak Mockito, które polegają na implementacji interfejsu lub dziedziczeniu, aby zastępować zachowania podczas testowania. W przypadku testów jednostkowych możesz włączyć korzystanie z funkcji Mock Maker Inline w Mockito, która umożliwia tworzenie atrap klas i metod finalnych. Możesz też użyć wtyczki kompilatora All-Open aby otworzyć dowolną klasę Kotlin i jej elementy, które chcesz przetestować w ramach procesu kompilacji. Główną zaletą korzystania z tej wtyczki jest to, że działa ona zarówno z testami jednostkowymi, jak i testami z instrumentacją.

Więcej informacji

Więcej informacji o korzystaniu z Kotlina znajdziesz pod tymi linkami: