Wdrożenie Kotlin w dużych zespołach

Przejście na nowy język może być trudnym zadaniem. Przepis na sukces polega na tym, by zacząć powoli, przechodzić kolejno do kolejnych części i często przeprowadzać testy, by pomóc zespołowi osiągnąć sukces. Kotlin ułatwia migrację, ponieważ kompiluje się do kodu bajtowego JVM i jest w pełni zgodny z Javą.

Budowanie zespołu

Pierwszym krokiem przed migracją jest wypracowanie spójności bazowej dla zespołu. Oto kilka wskazówek, które mogą pomóc w przyspieszeniu nauki Twojego zespołu.

Grupy do badania formularzy

Grupy uczniów są skutecznym sposobem na ułatwienie nauki i zatrzymania. Badania sugerują, że powtarzanie zdobytych informacji w grupie ułatwia utrwalenie materiału. Uzyskaj książkę z Kotlinem lub inne materiały do nauki dla każdego członka grupy i poproś grupę o przeczytanie co tydzień kilku rozdziałów. Podczas każdego spotkania grupa powinna porównać wnioski zdobyta i omówić jakieś pytania lub obserwacje.

Kształtowanie kultury nauczania

Nie każdy może uważać się za nauczyciela, ale każdy może być nauczycielem. Od technologii lub kierownika zespołu po indywidualnego współtwórcy – każdy może zachęcać do tworzenia środowiska edukacyjnego, które przyczyni się do osiągnięcia sukcesu. Jednym ze sposobów jest organizowanie okresowych prezentacji, w których jedna osoba z zespołu może podzielić się nabytą wiedzą lub dzielić się wiedzą. Możesz wykorzystać grupę edukacyjną, prosząc ochotników o publikowanie nowego rozdziału co tydzień, aż dojdziesz do momentu, w którym Twój zespół poczuje się swobodnie w danym języku.

Wyznacz mistrza

Na koniec wyznacz osobę, która poprowadzi naukę. Ta osoba może pełnić rolę eksperta z danej dziedziny (SME), gdy rozpoczniesz proces wdrażania. Ważne jest, aby dodawać tę osobę do wszystkich spotkań ćwiczeniowych związanych z Kotlinem. Najlepiej jeśli ta osoba już pasjonuje się Kotlinem i ma wiedzę praktyczną.

Powolna integracja

Zacznij powoli i strategicznie myśleć o tym, które części ekosystemu powinny zacząć działać w pierwszej kolejności. Często najlepiej jest utworzyć jedną aplikację w obrębie organizacji, a nie aplikację flagową. Każda sytuacja jest inna, jeśli chodzi o migrację wybranej aplikacji. Oto kilka typowych rozwiązań, od których warto zacząć.

Model danych

Twój model danych prawdopodobnie składa się z wielu informacji o stanie oraz kilku metod. Model danych może też mieć popularne metody, np. toString(), equals() i hashcode(). Metody te można zwykle łatwo przenieść i przetestować w oderwaniu od siebie.

Załóżmy na przykład, że taki fragment kodu 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 + '\'' +
               '}';
   }
}

Możesz zastąpić klasę Java jednym wierszem kodu Kotlin, jak pokazano poniżej:

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

Kod ten możesz następnie przetestować za pomocą jednostki w porównaniu z obecnym zestawem testów. Chodzi o to, aby zacząć od 1 modelu naraz i klasy przejścia, które są w większości stanowe, a nie zachowują się. Testuj je regularnie.

Migracja testów

Kolejnym punktem wyjścia do rozważenia jest przekonwertowanie istniejących testów i rozpoczęcie pisania nowych testów w Kotlin. To da zespołowi czas na przyzwyczajenie się do języka przed napisaniem kodu, który zamierzasz wysłać z aplikacją.

Przenieś metody narzędziowe do funkcji rozszerzeń

Wszystkie statyczne klasy narzędziowych (StringUtils, IntegerUtils, DateUtils, YourCustomTypeUtils itd.) mogą być reprezentowane jako funkcje rozszerzenia Kotlin i używane przez istniejącą bazę 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
   }

}

Metody te mogą być następnie używane w innym miejscu w aplikacji, jak pokazano w tym przykładzie:

...

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

...

Za pomocą funkcji rozszerzenia Kotlin możesz udostępnić ten sam interfejs Utils elementom wywołującym Java, a jednocześnie zaoferować bardziej zwięzły interfejs API dla rosnącej bazy kodu Kotlin.

Aby to zrobić, możesz zacząć od przekonwertowania tej klasy Utils na Kotlin przy użyciu automatycznej konwersji udostępnianej przez IDE. Przykładowe dane wyjściowe mogą wyglądać mniej więcej 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, poprzedź nazwę każdej funkcji typem, do którego ma być stosowana ta funkcja, i użyj tego atrybutu, aby odwołać się do typu wewnątrz funkcji, jak 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 u góry pliku źródłowego, by skompilowana nazwa była zgodna z resztą aplikacji, jak w tym przykładzie:

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

Ostateczna wersja powinna wyglądać mniej więcej 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
}

Te funkcje można teraz wywoływać za pomocą Javy lub Kotlin z konwencjami pasującymi do poszczególnych języków.

Kotlin

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

Java

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

Dokończ migrację

Gdy Twój zespół nauczy się korzystania z Kotlin i przeniesiesz mniejsze obszary, możesz zająć się większymi komponentami, takimi jak fragmenty, aktywności, obiekty ViewModel i inne klasy powiązane z logiką biznesową.

co należy wziąć pod uwagę

Podobnie jak Java, Kotlin ma swój własny idiomatyczny styl, który odzwierciedla jego zwięzłość. Może się jednak okazać, że na początku kod Kotlin tworzony przez Twój zespół wygląda bardziej jak kod Java, który zastępuje. Zmienia się to z czasem w miarę zwiększania doświadczenia Twojego zespołu w Kotlin. Pamiętaj, że kluczem do sukcesu jest stopniowa zmiana.

Oto kilka czynności, które możesz wykonać, aby uzyskać spójność w miarę powiększania się bazy kodu Kotlin:

Typowe standardy kodowania

Pamiętaj, aby na wczesnym etapie określić standardowy zestaw konwencji kodowania. Możesz odbiegać od przewodnika stylistycznego Androida Kotlin tam, gdzie ma to sens.

Narzędzia do analizy statycznej

egzekwuj standardy kodowania wyznaczone dla Twojego zespołu, używając Android Lint i innych narzędzi do analizy statycznej. klint, zewnętrzny linter Kotlin, oferuje również dodatkowe reguły dla usługi Kotlin.

Tryb ciągłej integracji

Zadbaj o zgodność z popularnymi standardami kodowania i zapewnij wystarczający zasięg testów kodu Kotlin. Udział w tej części zautomatyzowanego procesu kompilacji może pomóc w zapewnieniu spójności i zachowania zgodności z tymi standardami.

Interoperacyjność

Kotlin w większości bezproblemowo współpracuje z Javą, ale zwróć uwagę na poniższe kwestie.

Dopuszczalność wartości null

Kotlin wykorzystuje adnotacje dotyczące wartości null w skompilowanym kodzie, aby wnioskować o wartości null po stronie Kotlin. Jeśli nie podasz adnotacji, Kotlin domyślnie ustawia typ platformy, który może być traktowany jako typ null lub niedopuszczający wartości null. Jeśli jednak nie zachowasz ostrożności, może to spowodować problemy z NullPointerException w czasie działania.

Wdrażanie nowych funkcji

Kotlin udostępnia wiele nowych bibliotek i cukru składniowego, które pozwalają ograniczyć powtarzalność, co przyspiesza programowanie. Mimo to zachowaj ostrożność i metody korzystania ze standardowych funkcji biblioteki Kotlin, takich jak funkcje zbierania danych, korontyny i lambda.

Oto typowa pułapka, z którą natrafiają nowi programiści Kotlin. Przyjmijmy taki kod 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()
}

Intencją w tym przykładzie jest wykonanie foo.baz() i foo.zap(), jeśli właściwość nullableFoo nie ma wartości null, przez co unika się NullPointerException. Ten kod działa zgodnie z oczekiwaniami, ale jest mniej intuicyjny w odczyty niż prosty test null i smart cast, jak widać 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

Klasy i ich funkcje w Kotlin są domyślnie zamknięte i nie można ich przedłużyć. Musisz otwarcie otworzyć klasy i funkcje, które chcesz przekształcić w podklasy. Takie zachowanie wynika z decyzji dotyczącej projektu języka, która promuje kompozycję zamiast dziedziczenia. Kotlin ma wbudowaną obsługę wdrażania zachowań przez przekazywanie dostępu, aby ułatwić kompozycję.

Takie zachowanie stanowi problem dla żartobliwych platform, takich jak Mockito, które polegają na implementacji interfejsu lub dziedziczeniu, aby zastąpić zachowania podczas testowania. W testach jednostkowych możesz włączyć funkcję Mock Maker Inline (wbudowana funkcja Mock Maker) dostępna w Mockito, która umożliwia imitowanie klas i metod końcowych. Możesz też użyć wtyczki do kompilatora All-Open, aby otworzyć dowolną klasę Kotlin i jej elementy, które chcesz przetestować w ramach procesu kompilacji. Główną zaletą korzystania z niej jest to, że działa ona zarówno z testami jednostowymi, jak i instrumentalnymi.

Więcej informacji

Więcej informacji o korzystaniu z Kotlin znajdziesz tutaj: