Boas práticas e padrões · Padrões estruturais · 5 min de leitura
Decorator
Acrescenta comportamento (cache, log, retry) envolvendo um objeto com outro da mesma interface, sem mexer no original.
Arraste para o lado para ver o diagrama inteiro.
Como funciona
O decorador implementa a mesma interface do objeto decorado, recebe-o no construtor e faz algo antes e/ou depois de delegar. Como todos têm o mesmo tipo, dá para empilhar em qualquer ordem. BufferedInputStream do Java e os interceptors do OkHttp seguem a mesma ideia.
Em Kotlin
A delegação de interface (class CacheRepo(r: Repo) : Repo by r) gera todos os métodos delegados automaticamente: você sobrescreve só o que decora.
Cuidados
A ordem importa (cache antes do retry evita retentar o que já está em cache). Muitas camadas dificultam o debug — documente a montagem em um lugar só (módulo de DI).
No código
interface CotacaoRepo { suspend fun dolar(): Double }
class ApiCotacaoRepo(private val api: Api) : CotacaoRepo { override suspend fun dolar() = api.cotacao("USD") }
class CacheCotacaoRepo(private val origem: CotacaoRepo, private val relogio: Clock) : CotacaoRepo by origem {
private var valor: Double? = null
private var em: Instant = Instant.MIN
override suspend fun dolar(): Double {
val agora = relogio.instant()
valor?.takeIf { Duration.between(em, agora) < Duration.ofMinutes(5) }?.let { return it }
return origem.dolar().also { valor = it; em = agora }
}
}
val repo: CotacaoRepo = CacheCotacaoRepo(ApiCotacaoRepo(api), Clock.systemUTC()) O ApiCotacaoRepo não foi tocado. O relógio injetado permite testar a expiração do cache sem esperar 5 minutos.
Em resumo
- Mesma interface, comportamento extra.
- Empilhável: log + cache + retry.
- Respeita o aberto/fechado.
- Kotlin: delegação com by elimina boilerplate.