
Eles dizem que "os padrões de design são soluções alternativas para as deficiências de uma linguagem de programação específica". Uma proposição interessante, se não tivesse sido dita pelos apologistas Lisp e Schema .
Mas parece que os desenvolvedores da linguagem Kotlin levaram essa afirmação a sério.
Singleton
Obviamente, o primeiro padrão que vem à mente é Loner. E é construído diretamente no idioma na forma da palavra-chave do objeto :
object JustSingleton { val value : String = "Just a value" }
Agora, o campo JustSingleton.value
estará acessível em qualquer lugar do pacote.
E não, isso não é inicialização estática, como pode parecer. Vamos tentar inicializar este campo com algum atraso dentro:
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") } }
Inicialização lenta ocorre na primeira chamada:
@org.testng.annotations.Test fun testSingleton() { println("Test started") for (i in 1..3) { val total = measureTimeMillis { println(SlowSingleton.value) } println("Took $total ms") } }
A saída é:
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
Observe que, se você não usar esse objeto, a operação levará 0 ms, embora o objeto ainda esteja definido no seu código.
val total = measureTimeMillis {
Saída:
Test started Took 0 ms Took 0 ms Took 0 ms
Decorador
Depois vem o decorador . Esse é um padrão que permite adicionar um pouco de funcionalidade a outras classes. Sim, o IntelliJ pode criá-lo para você. Mas Kotlin foi ainda mais longe.
Que tal sempre que adicionamos uma nova chave no HashMap, recebemos uma mensagem sobre isso?
No construtor, você define uma instância na qual todos os métodos são delegados usando a palavra-chave by.
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") } } } }
Observe que podemos acessar os elementos de nosso mapa entre colchetes e usar todos os outros métodos da mesma maneira que em um HashMap regular.
@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
O objeto Companion facilita a implementação do Método de Fábrica . Esse é o padrão pelo qual o objeto controla seu processo de inicialização para ocultar alguns segredos dentro de si.
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) } } }
Agora ninguém pode mudar a idade da 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) }
Estratégia
O último de hoje é Estratégia . Como o Kotlin possui funções de alta ordem , a implementação desse padrão também é muito simples:
class UncertainAnimal { var makeSound = fun () { println("Meow!") } }
E mude dinamicamente o comportamento:
@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) }
Observe que esse é realmente um padrão de estratégia e você não pode alterar a assinatura do método (olá, JS!)
// Won't compile! someAnimal.makeSound = fun (message : String) { println("$message") }
Todo o código está disponível na minha página do GitHub .
E se você estiver interessado em aprender mais sobre o Kotlin e seus padrões de design internos, há um ótimo livro, "Kotlin in Action" . Você vai gostar mesmo que não planeje usar esse idioma em um futuro próximo (embora não haja motivo para não fazê-lo).