Princípio da Substituição de Liskov (LSP)
Definição
O Princípio da Substituição de Liskov (Liskov Substitution Principle - LSP) estabelece que subclasses devem poder ser usadas no lugar de suas superclasses sem alterar o comportamento esperado do sistema.
Isso significa que uma classe derivada deve preservar o contrato da classe base: seus métodos devem manter as mesmas expectativas e efeitos. Se isso não acontece, a substituição causa comportamentos inesperados — violando o LSP.
Esse é o terceiro princípio do SOLID, um conjunto de boas práticas para design orientado a objetos.
Exemplo prático (Python)
A classe Retangulo
permite configurar largura e altura de
forma independente e calcula a área corretamente:
class Retangulo:
def __init__(self, largura, altura):
self.largura = largura
self.altura = altura
def definir_largura(self, largura):
self.largura = largura
def definir_altura(self, altura):
self.altura = altura
def calcular_area(self):
return self.largura * self.altura
Em seguida, criamos uma subclasse Quadrado
que tenta adaptar
o comportamento de Retangulo
, mas com a restrição de que os
dois lados sempre devem ser iguais:
class Quadrado(Retangulo):
def definir_largura(self, valor):
self.largura = valor
self.altura = valor
def definir_altura(self, valor):
self.altura = valor
self.largura = valor
Veja o comportamento esperado com Retangulo
:
r = Retangulo(5, 4)
print(r.calcular_area()) # Saída: 20
E o comportamento com Quadrado
:
q = Quadrado(5, 4) # Problema: um quadrado deveria ter lados iguais!
print(q.calcular_area()) # Saída: 16 — comportamento inesperado
Embora Quadrado
herde de Retangulo
, ele
não respeita o contrato da superclasse. O método
calcular_area()
deixa de refletir as dimensões configuradas,
violando o LSP. O construtor de Retangulo
aceita lados
diferentes, mas essa lógica não se aplica corretamente a um quadrado — o
que compromete a substituição segura.
Implementação correta que respeita o LSP
Em vez de fazer Quadrado
herdar de Retangulo
, o
mais adequado é criar uma interface comum para formas geométricas e fazer
com que cada classe implemente sua própria lógica de área:
from abc import ABC, abstractmethod
# Interface comum
class Forma(ABC):
@abstractmethod
def calcular_area(self):
pass
# Implementação para Retângulo
class Retangulo(Forma):
def __init__(self, largura, altura):
self.largura = largura
self.altura = altura
def calcular_area(self):
return self.largura * self.altura
# Implementação para Quadrado
class Quadrado(Forma):
def __init__(self, lado):
self.lado = lado
def calcular_area(self):
return self.lado * self.lado
# Exemplos de uso
retangulo = Retangulo(3, 6)
quadrado = Quadrado(4)
print(f"Área do retângulo: {retangulo.calcular_area()}")
print(f"Área do quadrado: {quadrado.calcular_area()}")
Dessa forma, Quadrado
e Retangulo
não violam o
LSP, pois
nenhum deles depende da lógica interna do outro. Cada um
cumpre seu contrato de forma independente, sem alterar o comportamento
esperado do sistema.
Consequências de ignorar o LSP
- Quebra funcionalidades ao substituir subclasses
- Introduz bugs difíceis de rastrear
- Viola o contrato de uso da superclasse
- Enfraquece a robustez de sistemas orientados a objetos