Patrons de conception en Kotlin avec exemples : de A à Z
Les patrons de conception (design patterns) sont des solutions éprouvées pour des problèmes de développement typiques. En Kotlin, grâce à sa syntaxe concise et à ses puissantes fonctionnalités (data class, object, extension functions, coroutines), de nombreux patrons classiques sont implémentés plus simplement et plus élégamment qu'en Java. Dans cet article, nous analyserons les patrons clés en Kotlin, des patrons de création aux patrons comportementaux, avec des exemples de code concrets.
1. Patrons de création (Creational)
Ces patrons gèrent le processus de création d'objets, rendant le système indépendant de la manière dont ils sont créés.
1.1. Singleton (Singleton)
Garantit qu'une classe n'a qu'une seule instance. En Kotlin, le mot-clé object est utilisé pour cela.
object DatabaseConnection { val connectionString: String = "jdbc:postgresql://localhost:5432/mydb"
fun connect() { println("Connexion à $connectionString") }}
fun main() { DatabaseConnection.connect() println(DatabaseConnection.connectionString)}Quand l'utiliser : pour les gestionnaires de configuration, les pools de threads, les caches.
1.2. Factory Method (Méthode de fabrique)
Définit une interface pour créer un objet, mais permet aux sous-classes de modifier le type d'objet créé.
// Produit abstraitinterface Notification { fun send(message: String)}
// Produits concretsclass EmailNotification : Notification { override fun send(message: String) = println("Email : $message")}
class SMSNotification : Notification { override fun send(message: String) = println("SMS : $message")}
// Fabriqueabstract class NotificationFactory { abstract fun createNotification(): Notification
fun notifyUser(message: String) { val notification = createNotification() notification.send(message) }}
class EmailFactory : NotificationFactory() { override fun createNotification(): Notification = EmailNotification()}
class SMSFactory : NotificationFactory() { override fun createNotification(): Notification = SMSNotification()}
fun main() { val factory: NotificationFactory = EmailFactory() factory.notifyUser("Bonjour depuis Kotlin !")}Quand l'utiliser : lorsque le type des objets à créer est inconnu à l'avance.
1.3. Builder (Constructeur)
Permet de créer des objets complexes étape par étape. En Kotlin, il est bien implémenté via apply et les arguments nommés.
data class Pizza( val size: String, val cheese: Boolean = false, val pepperoni: Boolean = false, val mushrooms: Boolean = false) { // Builder intégré class Builder(private val size: String) { private var cheese: Boolean = false private var pepperoni: Boolean = false private var mushrooms: Boolean = false
fun cheese(value: Boolean) = apply { cheese = value } fun pepperoni(value: Boolean) = apply { pepperoni = value } fun mushrooms(value: Boolean) = apply { mushrooms = value }
fun build() = Pizza(size, cheese, pepperoni, mushrooms) }}
fun main() { val pizza = Pizza.Builder("Grande") .cheese(true) .pepperoni(true) .build() println(pizza)}Quand l'utiliser : lorsqu'il y a un grand nombre de paramètres optionnels.
2. Patrons structurels (Structural)
Ces patrons définissent des moyens de construire des classes et des objets en structures plus grandes.
2.1. Adapter (Adaptateur)
Permet à des objets avec des interfaces incompatibles de fonctionner ensemble. En Kotlin, il est souvent utilisé via les extension functions.
// Ancienne interfaceinterface OldLogger { fun logMessage(msg: String)}
// Nouvelle interfaceinterface NewLogger { fun log(level: Stri