Boas práticas e padrões · Princípios SOLID · 5 min de leitura
L — Princípio da substituição de Liskov (LSP)
Um subtipo precisa funcionar em qualquer lugar onde o tipo base funciona, sem surpresas para quem chama.
Arraste para o lado para ver o diagrama inteiro.
De onde vem
Barbara Liskov (Turing Award 2008) e Jeannette Wing definiram em 1994 a subtipagem comportamental: o subtipo pode enfraquecer pré-condições e fortalecer pós-condições, nunca o contrário, e precisa preservar as invariantes do tipo base.
Sintomas
- Método sobrescrito que lança
UnsupportedOperationException. if (x is Subtipo)no código cliente para tratar um caso especial.- O clássico Quadrado que herda de Retângulo e quebra
setLargura.
Como corrigir
Modele pelo comportamento, não pela taxonomia do mundo real: separe Ave de Voadora, ou use composição. Testes de contrato (os mesmos testes rodando contra todas as implementações) pegam a quebra cedo.
No código: antes e depois
open class Ave { open fun voar() = println("voando") }
class Pinguim : Ave() {
override fun voar() = throw UnsupportedOperationException()
}
fun migrar(aves: List<Ave>) = aves.forEach { it.voar() } // quebra com Pinguim interface Ave { val nome: String }
interface Voadora : Ave { fun voar() }
class Pardal(override val nome: String = "pardal") : Voadora { override fun voar() = println("voando") }
class Pinguim(override val nome: String = "pinguim") : Ave
fun migrar(aves: List<Voadora>) = aves.forEach { it.voar() } // o tipo já garante O compilador passa a impedir o erro: só quem realmente voa entra na lista de migração.
Em resumo
- Subtipo não pode exigir mais nem entregar menos.
- Exceções "não suportado" denunciam a violação.
- Modele por comportamento, não pela biologia.
- Testes de contrato para todas as implementações.