Réusinage de code legacy en Kotlin : stratégie et exemples
Travailler avec du code legacy (ancien) est une partie inévitable de la vie de tout développeur. Souvent, ce code est écrit en Java, mais même les projets utilisant les premières versions de Kotlin peuvent contenir des « odeurs » (code smells) et des erreurs architecturales. Dans cet article, nous allons détailler une stratégie étape par étape pour le réusinage de code legacy en Kotlin, fournir des exemples concrets de transformations et expliquer comment éviter les erreurs typiques.
1. Préparation au réusinage : la sécurité avant tout
La première et la plus importante étape est d'assurer la sécurité des modifications. Sans une bonne couverture de tests, tout réusinage se transforme en jeu de roulette russe.
- Ajoutez des tests unitaires. S'il n'y a pas de tests, écrivez-en pour les parties critiques du code. Utilisez les frameworks
JUnit 5etMockKpour Kotlin. - Figez le comportement. Écrivez des tests qui vérifient le comportement actuel du système (characterization tests), même s'il semble incorrect. Cela permettra de ne pas casser la logique lors du réusinage.
- Utilisez un système de contrôle de version. Faites des commits après chaque étape logiquement terminée afin de pouvoir revenir en arrière.
Exemple d'un test simple pour une fonction legacy en Kotlin :
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
class LegacyServiceTest {
@Test
fun `test calculateTotal`() {
val service = LegacyService()
val result = service.calculateTotal(listOf(10.0, 20.0, 30.0))
assertEquals(60.0, result)
}
}
2. Migration progressive de Java vers Kotlin
Si votre code legacy est écrit en Java, n'essayez pas de tout réécrire d'un coup. Utilisez des convertisseurs automatiques (par exemple, celui intégré dans IntelliJ IDEA : Code → Convert Java File to Kotlin File), puis améliorez manuellement le résultat.
Les points principaux sur lesquels se concentrer après la conversion :
- Null safety. Remplacez
@Nullableet@NonNullpar les opérateurs?et!!(ce dernier seulement si vous êtes sûr de l'absence de null). - Data classes. Au lieu des classes Java avec
equals()ethashCode(), utilisezdata class. - Fonctions d'extension. Extrayez les méthodes statiques utilitaires dans des extension functions.
Exemple de transformation d'une classe Java en Kotlin :
// Java
public class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() { return name; }
public int getAge() { return age; }
}
// Kotlin (après réusinage)
data class User(val name: String, val age: Int)
3. Amélioration de l'architecture : se débarrasser du code « spaghetti »
Le code legacy souffre souvent de violations des principes SOLID. Voici trois techniques concrètes qui aideront à remettre le code en ordre :
3.1. Séparation des responsabilités (Single Responsibility)
Trouvez les classes qui en font trop. Par exemple, un « Objet Dieu » (God Object) qui stocke les données, les valide et envoie des requêtes. Divisez-le en plusieurs classes avec une zone de responsabilité claire.
3.2. Remplacement des longues chaînes if-else par when
Kotlin fournit un opérateur when puissant qui rend le code plus lisible :
// Avant réusinage
fun getStatusText(status: Int): String {
if (status == 0) return "Pending"
else if (status == 1) return "Active"
else if (status == 2) return "Blocked"
else return "Unknown"
}
// Après réusinage
fun getStatusText(status: Int): String = when (status) {
0 -> "Pending"
1 -> "Active"
2 -> "Blocked"
else -> "Unknown"
}