Pular para o conteúdo

Boas práticas e padrões · Padrões comportamentais · 5 min de leitura

State e máquinas de estado

Modela cada estado como um tipo, com as transições permitidas explícitas — adeus a combinações impossíveis de booleanos.

Criado → Pago: pagarpagar1Pago → Enviado: enviar2Enviado → Entregue: entregar3Criado → Cancelado: cancelarcancelarPago → Cancelado: estornarestornarCriadoCriadoPagoPagoEnviadoEnviadoEntregueEntregueCanceladoCancelado

Arraste para o lado para ver o diagrama inteiro.

Só as setas desenhadas existem: um pedido entregue não pode ser cancelado, e o código não deixa compilar essa transição.

O problema dos booleanos

isPago, isEnviado, isCancelado geram 8 combinações, e várias são impossíveis ("enviado e não pago"). Um tipo por estado torna os estados inválidos irrepresentáveis.

Em Kotlin

sealed interface + when exaustivo é a forma idiomática. Cada estado carrega só os dados que fazem sentido nele (o código de rastreio existe apenas em Enviado). O mesmo vale para o estado da UI: Carregando, Sucesso(dados), Erro(mensagem).

No código: antes e depois

✗ AntesKotlin
data class Pedido(val id: Long, var pago: Boolean, var enviado: Boolean, var cancelado: Boolean, var rastreio: String?)
// enviado = true, pago = false, rastreio = null... é válido?
✓ DepoisKotlin
sealed interface Pedido {
  val id: Long
  data class Criado(override val id: Long) : Pedido
  data class Pago(override val id: Long, val recibo: String) : Pedido
  data class Enviado(override val id: Long, val recibo: String, val rastreio: String) : Pedido
  data class Cancelado(override val id: Long, val motivo: String) : Pedido
}

fun Pedido.Pago.enviar(rastreio: String) = Pedido.Enviado(id, recibo, rastreio)
fun Pedido.Criado.cancelar(motivo: String) = Pedido.Cancelado(id, motivo)

fun rotulo(p: Pedido) = when (p) {
  is Pedido.Criado -> "Aguardando pagamento"
  is Pedido.Pago -> "Pago"
  is Pedido.Enviado -> "A caminho: ${p.rastreio}"
  is Pedido.Cancelado -> "Cancelado"
}

enviar() só existe em Pedido.Pago: tentar enviar um pedido não pago nem compila. O when é exaustivo.

Em resumo

Referências

Continue aprendendo

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