Padrão de delegação em Kotlin

Padrão de delegação em Kotlin

1. Visão geral

Existem muitos casos de uso em que a delegação é preferível à herança. O Kotlin tem um ótimo suporte no nível do idioma para isso.

Neste tutorial,we’ll talk about Kotlin’s native support for the delegation pattern e veja-o em ação.

2. Implementação

Primeiro, vamos supor que temos um exemplo de código com a estrutura abaixo em uma biblioteca de terceiros:

interface Producer {

    fun produce(): String
}

class ProducerImpl : Producer {

    override fun produce() = "ProducerImpl"
}

Em seguida,let’s decorate the existing implementationusing the “by” keyword e adicione o processamento adicional necessário:

class EnhancedProducer(private val delegate: Producer) : Producer by delegate {

    override fun produce() = "${delegate.produce()} and EnhancedProducer"
}

Portanto, neste exemplo, indicamos que a classeEnhancedProducer encapsulará um objetodelegate do tipoProducer. E também pode usar a funcionalidade da implementação deProducer.

Finalmente, vamos verificar se funciona conforme o esperado:

val producer = EnhancedProducer(ProducerImpl())
assertThat(producer.produce()).isEqualTo("ProducerImpl and EnhancedProducer")

3. Casos de Uso

Agora, vamos ver dois casos de uso comuns para o padrão de delegação.

Primeiro, podemos usar o padrão de delegaçãoto implement multiple interfaces using existing implementations:

class CompositeService : UserService by UserServiceImpl(), MessageService by MessageServiceImpl()

Em segundo lugar, podemos usar delegaçãoto enhance an existing implementation.

O último é o que fizemos na seção anterior. Mas, um exemplo mais real como o abaixo é especialmente útil quando não podemos modificar uma implementação existente - por exemplo, código de biblioteca de terceiros:

class SynchronizedProducer(private val delegate: Producer) : Producer by delegate {

    private val lock = ReentrantLock()

    override fun produce(): String {
        lock.withLock {
            return delegate.produce()
        }
    }
}

4. Delegação não é herança

Agora, precisamos sempre lembrar quethe delegate knows nothing about the decorator. Portanto, não devemos tentar a abordagemGoF Template Method-like com eles.

Vamos considerar um exemplo:

interface Service {

    val seed: Int

    fun serve(action: (Int) -> Unit)
}

class ServiceImpl : Service {

    override val seed = 1

    override fun serve(action: (Int) -> Unit) {
        action(seed)
    }
}

class ServiceDecorator : Service by ServiceImpl() {
    override val seed = 2
}

Aqui, o delegado (ServiceImpl) usa uma propriedade definida na interface comum e nós a substituímos no decorador (ServiceDecorator). No entanto, isso não afeta o processamento do delegado:

val service = ServiceDecorator()
service.serve {
    assertThat(it).isEqualTo(1)
}

Por fim, é importante observar que, em Kotlin,we can delegate not only to interfaces but to separate properties as well.

5. Conclusão

Neste tutorial, falamos sobre a delegação de interface do Kotlin - quando deve ser usada, como configurá-la e suas advertências.

Como de costume, o código-fonte completo deste artigo está disponívelover on GitHub.