Patrones de diseño de Kotlin

Patrones de diseño de Kotlin


Dicen que "los patrones de diseño son soluciones para las deficiencias de un lenguaje de programación particular". Una propuesta interesante, si no hubiera sido dicha por los apologistas Lisp y Schema .


Pero parece que los desarrolladores del lenguaje Kotlin tomaron esta declaración realmente en serio.


Singleton


Por supuesto, el primer patrón que viene a la mente es Loner. Y está integrado directamente en el lenguaje en forma de palabra clave de objeto :


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

Ahora se podrá acceder al campo JustSingleton.value desde cualquier parte del paquete.


Y no, esto no es una inicialización estática, como podría parecer. Intentemos inicializar este campo con algún retraso en el interior:


 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") } } 

La inicialización diferida ocurre en la primera llamada:


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

El resultado es:


 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 

Tenga en cuenta que si no utiliza este objeto, la operación dura 0 ms, aunque el objeto todavía está definido en su código.


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

Salida:


 Test started Took 0 ms Took 0 ms Took 0 ms 

Decorador


Luego viene el decorador . Este es un patrón que le permite agregar un poco de funcionalidad además de alguna otra clase. Sí, IntelliJ puede crearlo por ti. Pero Kotlin fue aún más lejos.


¿Qué tal cada vez que agregamos una nueva clave en el HashMap, recibimos un mensaje al respecto?


En el constructor, define una instancia a la que se delegan todos los métodos mediante la palabra clave 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") } } } } 

Tenga en cuenta que podemos acceder a los elementos de nuestro mapa entre corchetes y utilizar todos los demás métodos de la misma manera que en un HashMap normal.


 @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étodo de fábrica


El objeto Companion facilita la implementación del Método Factory . Este es el patrón mediante el cual el objeto controla su proceso de inicialización para ocultar algunos secretos dentro de sí mismo.


 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) } } } 

Ahora nadie puede cambiar la edad 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) } 

Estrategia


El último para hoy es Estrategia . Dado que Kotlin tiene funciones de alto orden , implementar este patrón también es muy simple:


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

Y cambie dinámicamente el comportamiento:


 @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) } 

Tenga en cuenta que este es realmente un patrón de estrategia, y no puede cambiar la firma del método (¡hola, JS!)


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

Todo el código está disponible en mi página de GitHub .


Y si está interesado en aprender más sobre Kotlin y sus patrones de diseño integrados, hay un gran libro, "Kotlin en acción" . Le gustará incluso si no planea usar este idioma en un futuro próximo (aunque no hay razón para no hacerlo).

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


All Articles