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, musisz zacząć powoli, pracować etapami i często testować. Kotlin ułatwia migrację, ponieważ kompiluje się do kodu bajtowego JVM i jest w pełni interoperacyjny z Javą.

Budowanie zespołu

Pierwszym krokiem przed migracją jest zbudowanie wspólnej podstawowej wiedzy w zespole. Oto kilka wskazówek, które mogą Ci pomóc w przyspieszeniu nauki zespołu.

Tworzenie grup roboczych

Grupy robocze 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 lidera zespołu lub technologii po indywidualnego współpracownika – każdy może zachęcać do tworzenia środowiska uczenia się, które pomoże osiągnąć sukces. Jednym ze sposobów na ułatwienie tego jest organizowanie okresowych prezentacji, podczas których jedna osoba z zespołu opowiada o tym, czego się nauczyła lub czym chce się podzielić. Możesz wykorzystać grupę roboczą, 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ę. Gdy zaczniesz proces wdrażania, ta osoba może pełnić rolę eksperta w danej dziedzinie (SME). Ważne jest, aby ta osoba uczestniczyła we wszystkich spotkaniach dotyczących Kotlina. Najlepiej, jeśli ta osoba pasjonuje się Kotlinem i ma pewną wiedzę praktyczną.

Powolne wdrażanie

Kluczowe znaczenie ma powolne rozpoczynanie i strategiczne myślenie o tym, które części ekosystemu należy przenieść w pierwszej kolejności. Często najlepiej jest ograniczyć się do jednej aplikacji w organizacji, a nie do aplikacji flagowej. 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(). Te metody można zwykle łatwo przenieść i przetestować jednostkowo 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 tutaj:

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

Ten kod można następnie przetestować jednostkowo w bieżącym zestawie testów. Chodzi o to, aby zacząć od małego, od jednego modelu naraz, i przenosić klasy, które są głównie stanem, a nie zachowaniem. Pamiętaj, aby często testować.

Migracja testów

Innym sposobem na rozpoczęcie jest przekonwertowanie istniejących testów i rozpoczęcie pisania nowych testów w Kotlinie. Dzięki temu Twój zespół będzie miał czas na oswojenie się z językiem przed napisaniem kodu, który planujesz dostarczyć z aplikacją.

Przenoszenie metod narzędziowych do funkcji rozszerzeń

Wszystkie statyczne klasy narzędziowe (StringUtils, IntegerUtils, DateUtils, YourCustomTypeUtils, itp.) można przedstawić jako funkcje rozszerzeń Kotlin 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ć następnie używane w innych miejscach aplikacji, jak pokazano w tym przykładzie:

...

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

...

Za pomocą funkcji rozszerzeń Kotlin 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ć podobnie do tych:

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 prefiks typu, 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ć podobnie do tej:

@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 za pomocą konwencji pasujących do każdego języka.

Kotlin

// ...

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

// ...

Java

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

Kończenie migracji

Gdy Twój zespół będzie się czuł swobodnie w Kotlinie i przeniesiesz mniejsze obszary, 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. Może się jednak okazać, że początkowo kod Kotlin tworzony przez Twój zespół będzie bardziej przypominał kod Java, który zastępuje. Zmieni się to z czasem, gdy zespół będzie miał więcej doświadczenia z Kotlinem. Pamiętaj, że kluczem do sukcesu są stopniowe zmiany.

Oto kilka rzeczy, które możesz zrobić, aby zachować spójność w miarę rozwoju bazy kodu Kotlin:

Typowe standardy kodowania

Na początku procesu wdrażania zdefiniuj standardowy zestaw konwencji kodowania. W uzasadnionych przypadkach możesz odbiegać od przewodnika po stylu Kotlin na Androidzie .

Narzędzia do analizy statycznej

Wymuszaj stosowanie standardów kodowania ustalonych dla zespołu za pomocą narzędzi Android lint i innych narzędzi do analizy statycznej. klint, narzędzie do sprawdzania kodu Kotlin innych firm, 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 dla 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 typ dopuszczający lub niedopuszczający wartości null. Jeśli nie będziesz uważać, może to jednak prowadzić do problemów z NullPointerException w czasie działania.

Wdrażanie nowych funkcji

Kotlin udostępnia wiele nowych bibliotek i ułatwień składniowych, które zmniejszają ilość kodu powtarzalnego, co pomaga zwiększyć szybkość tworzenia. Należy jednak zachować ostrożność i metodyczność podczas korzystania z funkcji biblioteki standardowej Kotlin, takich jak funkcje kolekcji, współprogramy i lambdy.

Oto bardzo częsta pułapka, w którą wpadają nowi deweloperzy Kotlina. Załóżmy, że masz ten kod w Kotlinie:

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ż proste sprawdzenie 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

Klasy i ich funkcje są domyślnie zamknięte na rozszerzenie w Kotlinie. Musisz jawnie otworzyć klasy i funkcje, które chcesz podklasować. To zachowanie jest decyzją projektową języka, która została podjęta w celu promowania kompozycji nad dziedziczeniem. Kotlin ma wbudowaną obsługę implementowania zachowania przez delegowanie co ułatwia kompozycję.

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 instrumentalnymi.

Więcej informacji

Więcej informacji o korzystaniu z Kotlina znajdziesz w tych artykułach: