Pourquoi le Builder en Kotlin n'est pas un simple "copier-coller" de Java ?
Si vous avez déjà programmé en Java, vous avez certainement rencontré le pattern Builder classique. Des dizaines de lignes de code, des classes statiques internes, des méthodes set et un build() final. Tout cela fonctionne, mais semble lourd.
Imaginez maintenant que vous passiez à Kotlin. Un langage créé pour être concis et expressif. Et vous êtes confronté à une tâche : créer un objet avec de nombreux paramètres, dont certains sont optionnels. Votre main se tend pour écrire le Builder classique de Java...
Stop ! En Kotlin, ce pattern peut être implémenté 10 fois plus simplement, plus élégamment et plus sûrement. Dans cet article, nous allons voir 4 façons d'implémenter le pattern Builder en Kotlin – de la plus simple à l'approche DSL avancée.
Ce contenu est basé sur une question réelle de Stack Overflow qui a recueilli des milliers de vues. Nous l'avons adapté pour un contenu éducatif afin que vous puissiez appliquer immédiatement ces connaissances en pratique.
Problème : quand a-t-on besoin d'un Builder ?
Imaginez que vous ayez une classe Pizza :
class Pizza( val size: String, val cheese: Boolean = false, val pepperoni: Boolean = false, val mushrooms: Boolean = false, val olives: Boolean = false, val extraCheese: Boolean = false)À première vue, tout va bien. Mais lorsque vous créez une pizza avec 5 paramètres optionnels, le code devient illisible :
val pizza = Pizza("Large", true, false, true, false, true)Chercher des yeux quel paramètre signifie quoi est pénible. C'est là que le pattern Builder vient à la rescousse.
Méthode 1. Builder classique de style Java (pour la compatibilité)
Si vous devez prendre en charge du code Java ou si vous voulez une API la plus explicite possible, vous pouvez utiliser l'approche classique :
class Pizza private constructor( val size: String, val cheese: Boolean, val pepperoni: Boolean, val mushrooms: Boolean, val olives: Boolean, val extraCheese: Boolean) { class Builder(private val size: String) { private var cheese: Boolean = false private var pepperoni: Boolean = false private var mushrooms: Boolean = false private var olives: Boolean = false private var extraCheese: Boolean = false
fun cheese(value: Boolean) = apply { cheese = value } fun pepperoni(value: Boolean) = apply { pepperoni = value } fun mushrooms(value: Boolean) = apply { mushrooms = value } fun olives(value: Boolean) = apply { olives = value } fun extraCheese(value: Boolean) = apply { extraCheese = value }
fun build() = Pizza(size, cheese, pepperoni, mushrooms, olives, extraCheese) }}
// Utilisation :val pizza = Pizza.Builder("Large") .cheese(true) .pepperoni(true) .mushrooms(true) .build()Avantages : code évident, compréhensible par les développeurs Java.
Inconvénients : beaucoup de code boilerplate, n'utilise pas les possibilités de Kotlin.
Méthode 2. Style Kotlin avec arguments nommés et valeurs par défaut
La façon la plus simple et souvent la meilleure est de ne pas utiliser de Builder du tout, mais de s'appuyer sur les possibilités du langage lui-même :
data class Pizza( val size: String, val cheese: Boolean = false, val pepperoni: Boolean = false, val mushrooms: Boolean = false, val olives: Boolean = false, val extraCheese: Boolean = false)
// Utilisation :val pizza = Pizza( size = "Large", cheese = true, pepperoni = true, mushrooms = true)C'est un code propre, lisible et type-safe. Les arguments nommés de Kotlin résolvent le problème des "nombres magiques" et de l'ordre des paramètres.