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.
Arraste para o lado para ver o diagrama inteiro.
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
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? 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
- Um tipo por estado, dados próprios em cada um.
- Transições permitidas ficam explícitas.
- when exaustivo: o compilador cobra os casos.
- Ótimo para UI: Carregando / Sucesso / Erro.