Disclaimer
Este texto foi inicialmente concebido pela IA Generativa em função da transcrição do episódio do nosso canal, Dev Eficiente. Se preferir acompanhar por vídeo, é só dar o play.
Introdução
O Princípio de Substituição de Liskov (LSP) é frequentemente citado como uma das bases do design orientado a objetos, mas sua definição pode parecer complexa à primeira vista. Para muitos desenvolvedores, as explicações teóricas sobre pré-condições e pós-condições, geralmente apresentadas em textos formais, não ajudam muito na compreensão prática. Neste post, vamos explorar o LSP de maneira simples e prática, usando exemplos de código para facilitar o entendimento.
O Que Diz o Princípio de Liskov?
De forma simplificada, o LSP afirma que uma subclasse deve ser substituível por sua classe pai sem que o comportamento esperado do sistema seja quebrado. Isso significa que, se uma classe filha não respeitar os contratos definidos pela classe pai, o sistema pode apresentar comportamentos inesperados.
Contratos: Pré-condições e Pós-condições
- Pré-condição: O que o método precisa para funcionar corretamente.
- Pós-condição: O que o método garante que será retornado após sua execução.
O LSP estipula que:
- Pré-condições não podem ser reforçadas na subclasse, ou seja, a subclasse não pode exigir mais do que a classe pai.
- Pós-condições não podem ser enfraquecidas, ou seja, a subclasse deve retornar resultados dentro das expectativas da classe pai.
Exemplificando o Princípio
Cenário Inicial: A Classe Base
Considere uma classe chamada ImpostoPadrão
que calcula impostos. Ela possui um método calculaImposto
que aceita um valor numérico positivo e retorna um valor positivo correspondente ao imposto:
public class ImpostoPadrão {
public double calculaImposto(double valor) {
if (valor <= 0) {
throw new IllegalArgumentException("Valor inválido");
}
// Implementação do cálculo (exemplo simplificado)
return valor * 0.1;
}
}
Essa classe define que:
- A pré-condição é que o valor seja positivo.
- A pós-condição é que o método sempre retorne um número maior que zero.
A Subclasse Problemática
Agora, imagine que criamos uma subclasse chamada ImpostoSobreServiço
que modifica o comportamento do método calculaImposto para aceitar valores maiores que 100. Essa alteração reforça a pré-condição, causando possíveis problemas:
public class ImpostoSobreServiço extends ImpostoPadrão {
@Override
public double calculaImposto(double valor) {
if (valor <= 100) {
throw new IllegalArgumentException("Valor inválido para serviço");
}
return valor * 0.15;
}
}
Se um código existente dependente da classe ImpostoPadrão
for reutilizado com a subclasse ImpostoSobreServiço
, ele poderá falhar ao passar valores entre 0 e 100, mesmo que esses valores sejam válidos para a classe pai.
Ajustando para Respeitar o LSP
Para respeitar o LSP, a subclasse deve aceitar pelo menos os mesmos valores que a classe pai aceita. Por exemplo, poderíamos ajustar a pré-condição para algo mais permissivo:
public class ImpostoSobreServiço extends ImpostoPadrão {
@Override
public double calculaImposto(double valor) {
if (valor <= 0) {
throw new IllegalArgumentException("Valor inválido");
}
return valor * 0.15;
}
}
Nesse caso, qualquer código que espera a classe ImpostoPadrão
funcionará perfeitamente com a subclasse, sem surpresas.
Ampliando o LSP para Outros Contextos
O LSP não se limita apenas à herança em linguagens orientadas a objetos. Ele também pode ser aplicado a contratos de APIs, interfaces e até mesmo a sistemas distribuídos. Por exemplo:
- Alterar um contrato de API para aceitar menos entradas ou retornar resultados inesperados pode quebrar consumidores existentes.
- Alterações em interfaces ou contratos devem considerar o impacto nos clientes dependentes.
Conclusão
O Princípio de Substituição de Liskov nos lembra de sempre projetar sistemas pensando nos contratos e nas expectativas dos clientes. Seja em classes, APIs ou interfaces, mudanças devem ser realizadas com cuidado para não causar surpresas desagradáveis. Respeitar o LSP não é apenas uma boa prática de design; é essencial para criar sistemas robustos e confiáveis.
Sobre a Jornada Dev + Eficiente
A Jornada Dev + Eficiente é um treinamento focado em fazer você crescer na carreira como uma pessoa cada vez mais especializada em Design e Arquitetura de Software.
A Jornada pavimenta este caminho fazendo com que você seja cada vez mais capaz de colocar código de qualidade em produção com cada vez mais velocidade.
Para conhecer mais, acesse https://deveficiente.com/kr/lp
Top comments (0)