Delegados Kotlin

Tempo de leitura: 4 minutes

A linguagem Kotlin é um pacote de muitos recursos incríveis. Depois de começar a usar os recursos do Kotlin, você os entenderá melhor. Nesta postagem, vamos explorar os Delegados Kotlin e como usá-los.

 

Introdução ao padrão de delegação

(Delegation Pattern)

Na engenharia de software, o padrão de delegação é um padrão de design orientado a objetos que permite a composição de objetos para obter a mesma reutilização de código como herança – Wikipedia

Podemos dizer que a delegação é uma alternativa à herança, no entanto, ambos fornecem um comportamento semelhante. Ambos nos permitem reutilizar o código, mas de uma maneira ligeiramente diferente. Em alguns casos, precisamos usar delegados em vez de herança

  • Onde uma classe não é projetada para herança – como quando uma classe é final, privada, etc.
  • Quando precisamos apenas de alguma funcionalidade da classe base, em vez de todos

Mas raramente vemos a implementação da Delegação, pois estamos habituados à facilidade de usar herança ou à falta de um bom suporte.

 

Delegação em Kotlin?

(Delegation)

Kotlin oferece suporte nativo à delegação e podemos fazer isso em uma única linha usando a palavra-chave “por”. Delegar significa passar a responsabilidade para outra classe ou método. Se houver uma interface que possui poucos métodos durante a implementação em uma classe, não precisamos fornecer a implementação de métodos de interface se usarmos por palavra-chave, pois todas as funções da interface serão delegadas ao objeto especificado usando essa palavra-chave. Vejamos um exemplo simples:

interface Base {
    fun print()
}

class BaseImpl(val x: Int) : Base {
    override fun print() { print(x) }
}

class Derived(b: Base) : Base by b

fun main() {
    val b = BaseImpl(10)
    Derived(b).print()
}

Aqui também temos a opção de selecionar funções da interface para fornecer nossa implementação personalizada na classe Derivada. Se não fornecemos nenhuma implementação de métodos na classe, esses métodos serão delegados ao objeto especificado usando por palavra-chave. Vamos modificar um pouco o exemplo acima

interface Base {
    fun printMessage()
    fun printMessageLine()
}

class BaseImpl(val x: Int) : Base {
    override fun printMessage() { print(x) }
    override fun printMessageLine() { println(x) }
}

class Derived(b: Base) : Base by b {
    override fun printMessage() { print("abc") }
}

fun main() {
    val b = BaseImpl(10)
    Derived(b).printMessage()
    Derived(b).printMessageLine()
}

Quando executamos o código acima e chamamos o método printMessage, ele imprime “abc” em vez de 10, no entanto, como não substituímos o método printMessageLine, ele imprime 10.

A saída do snippet acima é

abc
10

 

Delegados padrão Kotlin

(Standard Delegates)

A biblioteca padrão Kotlin fornece métodos de fábrica para vários tipos úteis de delegados.

 

lazy

fun <T> lazy(initializer: () -> T): Lazy<T>

Lazy é uma função que pega um lambda e retorna uma instância de Lazy <T>. Se quisermos que uma propriedade seja inicializada em seu primeiro acesso, podemos usar a inicialização lenta. Não é nada além de um delegado para inicializar preguiçosamente uma propriedade. Se a primeira inicialização de um valor lançar uma exceção, ele tentará reinicializar o valor no próximo acesso. A primeira chamada executa o lambda passado para lazy e memoriza o resultado, portanto, as chamadas subsequentes simplesmente retornam o resultado memorizado.

val lazyValue: Int by lazy {
    println("Bem-Vindo Lazy!!!")
    1
}

fun main() {
    println(lazyValue)
    println(lazyValue)
}

A saída para o snippet acima é

Bem-Vindo Lazy!!!
1
1

A avaliação das propriedades lazy é por padrão sincronizada, o que significa que a inicialização é feita em um thread onde todos os outros threads consomem o mesmo valor.

 

Delegates.Observable

inline fun <T> observable(
    initialValue: T,
    crossinline onChange: (property: KProperty<*>, oldValue: T, newValue: T) -> Unit): ReadWriteProperty<Any?, T>

Podemos usar este delegado para comportamento reativo em nossos aplicativos. Isso pode ser útil quando precisamos de um retorno de chamada para realizar alguma ação cada vez que o valor da propriedade muda. O observable() tem principalmente dois parâmetros:

initialValue – o valor inicial da propriedade,

onChange – o retorno de chamada que é chamado após a alteração da propriedade. O valor da propriedade já foi alterado quando este retorno de chamada é invocado (tem três parâmetros aos quais uma propriedade (property) está sendo atribuída, o valor antigo e o novo)

import kotlin.properties.Delegates

class User {
    var name: String by Delegates.observable("Valor Inicial") {
        prop, old, new ->
        println("$old -> $new")
    }
}

fun main() {
    val user = User()
    user.name = "Seu"
    user.name = "Nome"
}

A saída para o snippet acima é

Valor Inicial -> Seu
Seu -> Nome

 

Vetoable

inline fun <T> vetoable(
    initialValue: T,
    crossinline onChange: (property: KProperty<*>, oldValue: T, newValue: T) -> Boolean): ReadWriteProperty<Any?, T>

Vetoable é muito semelhante ao delegado acima, mas só nos permite atualizar um valor de propriedade se uma determinada condição for correspondida. Aqui, neste caso, o retorno de chamada é chamado antes que uma alteração no valor da propriedade seja tentada. Se o retorno de chamada retornar `true`, o valor da propriedade está sendo definido com o novo valor e se` false` for retornado, o valor da propriedade não será modificado.

var max: Int by Delegates.vetoable(1) { property, oldValue, newValue ->
    newValue > oldValue
}

println(max) // 1

max = 20
println(max) // 20

max = 3
println(max) // 20

A saída do snippet acima é

1
20
20

Como a última condição falhou, o valor não foi atualizado.

 

Resumo

Espero que agora você tenha uma ideia básica de como usar delegados (delegates) em Kotlin. Explore mais experimentando o código.