Modèles de conception Kotlin

Modèles de conception Kotlin


Ils disent que "les modèles de conception sont des solutions de contournement pour les lacunes d'un langage de programmation particulier." Une proposition intéressante, si seulement elle n'avait pas été dite par les apologistes Lisp et Schema .


Mais il semble que les développeurs du langage Kotlin aient vraiment pris cette déclaration à cœur.


Singleton


Bien sûr, le premier motif qui me vient à l'esprit est Loner. Et il est intégré directement dans le langage sous la forme du mot-clé objet :


object JustSingleton { val value : String = "Just a value" } 

Le champ JustSingleton.value sera désormais accessible de n'importe où dans le package.


Et non, ce n'est pas une initialisation statique, comme cela peut sembler. Essayons d'initialiser ce champ avec un certain retard à l'intérieur:


 object SlowSingleton { val value : String init { var uuid = "" val total = measureTimeMillis { println("Computing") for (i in 1..10_000_000) { uuid = UUID.randomUUID().toString() } } value = uuid println("Done computing in ${total}ms") } } 

L'initialisation paresseuse se produit au premier appel:


 @org.testng.annotations.Test fun testSingleton() { println("Test started") for (i in 1..3) { val total = measureTimeMillis { println(SlowSingleton.value) } println("Took $total ms") } } 

La sortie est:


 Test started Computing Done computing in 5376ms "45f7d567-9b3e-4099-98e6-569ebc26ecdf" Took 5377 ms "45f7d567-9b3e-4099-98e6-569ebc26ecdf" Took 0 ms "45f7d567-9b3e-4099-98e6-569ebc26ecdf" Took 0 ms 

Veuillez noter que si vous n'utilisez pas cet objet, l'opération prend 0 ms, bien que l'objet soit toujours défini dans votre code.


 val total = measureTimeMillis { //println(SlowSingleton.value) } 

Sortie:


 Test started Took 0 ms Took 0 ms Took 0 ms 

Décorateur


Vient ensuite le décorateur . Il s'agit d'un modèle qui vous permet d'ajouter un peu de fonctionnalités en plus d'une autre classe. Oui, IntelliJ peut le créer pour vous. Mais Kotlin est allé encore plus loin.


Que diriez-vous chaque fois que nous ajoutons une nouvelle clé dans le HashMap, nous recevons un message à ce sujet?


Dans le constructeur, vous définissez une instance à laquelle toutes les méthodes sont déléguées à l'aide du mot clé by.


 /** * Using `by` keyword you can delegate all but overridden methods */ class HappyMap<K, V>(val map : MutableMap<K, V> = mutableMapOf()) : MutableMap<K, V> by map{ override fun put(key: K, value: V): V? { return map.put(key, value).apply { if (this == null) { println("Yay! $key") } } } } 

Notez que nous pouvons accéder aux éléments de notre carte entre crochets et utiliser toutes les autres méthodes de la même manière que dans un HashMap ordinaire.


 @org.testng.annotations.Test fun testDecorator() { val map = HappyMap<String, String>() val result = captureOutput { map["A"] = "B" map["B"] = "C" map["A"] = "C" map.remove("A") map["A"] = "C" } assertEquals(mapOf("A" to "C", "B" to "C"), map.map) assertEquals(listOf("Yay! A", "Yay! B", "Yay! A"), (result)) } 

Méthode d'usine


L'objet Compagnon facilite l'implémentation de la méthode d'usine . C'est le modèle par lequel l'objet contrôle son processus d'initialisation afin de cacher certains secrets en lui-même.


 class SecretiveGirl private constructor(val age: Int, val name: String = "A girl has no name", val desires: String = "A girl has no desires") { companion object { fun newGirl(vararg desires : String) : SecretiveGirl { return SecretiveGirl(17, desires = desires.joinToString(", ")) } fun newGirl(name : String) : SecretiveGirl { return SecretiveGirl(17, name = name) } } } 

Maintenant, personne ne peut changer l'âge de SecretiveGirl:


 @org.testng.annotations.Test fun FactoryMethodTest() { // Cannot do this, constructor is private // val arya = SecretiveGirl(); val arya1 = SecretiveGirl.newGirl("Arry") assertEquals(17, arya1.age) assertEquals("Arry", arya1.name) assertEquals("A girl has no desires", arya1.desires) val arya2 = SecretiveGirl.newGirl("Cersei Lannister", "Joffrey", "Ilyn Payne") assertEquals(17, arya2.age) assertEquals("A girl has no name", arya2.name) assertEquals("Cersei Lannister, Joffrey, Ilyn Payne", arya2.desires) } 

La stratégie


Le dernier pour aujourd'hui est la stratégie . Étant donné que Kotlin a des fonctions de haut niveau , la mise en œuvre de ce modèle est également très simple:


 class UncertainAnimal { var makeSound = fun () { println("Meow!") } } 

Et changez dynamiquement le comportement:


 @org.testng.annotations.Test fun testStrategy() { val someAnimal = UncertainAnimal() val output = captureOutput { someAnimal.makeSound() someAnimal.makeSound = fun () { println("Woof!") } someAnimal.makeSound() } assertEquals(listOf("Meow!", "Woof!"), output) } 

Veuillez noter qu'il s'agit en effet d'un modèle de stratégie et que vous ne pouvez pas modifier la signature de la méthode (bonjour, JS!)


 // Won't compile! someAnimal.makeSound = fun (message : String) { println("$message") } 

Tout le code est disponible sur ma page GitHub .


Et si vous souhaitez en savoir plus sur Kotlin et ses modèles de conception intégrés, il existe un excellent livre, «Kotlin in Action» . Vous l'aimerez même si vous ne prévoyez pas d'utiliser cette langue dans un avenir proche (bien qu'il n'y ait aucune raison de ne pas le faire).

Source: https://habr.com/ru/post/fr421873/


All Articles