Pular para o conteúdo

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.

ViewModel → LogRepo1LogRepo → CacheRepo2CacheRepo → RetryRepo3RetryRepo → ApiRepo4ViewModelViewModelLogRepo — mede tempoLogRepomede tempoCacheRepo — guarda 5 minCacheRepoguarda 5 minRetryRepo — 3 tentativasRetryRepo3 tentativasApiRepo — redeApiReporedeRepo — mesma interfaceRepomesma interface

Arraste para o lado para ver o diagrama inteiro.

Cada camada implementa a mesma interface e delega para a próxima — como bonecas russas. O ViewModel não percebe a diferença.

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

✓ ExemploKotlin
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

Referências

Continue aprendendo

Todos os temas de boas práticas e padrões →