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.