SOLID — Princípio de Substituição de Liskov

Ruan Pelissoli
Trinca137
Published in
3 min readSep 2, 2019

Neste terceiro artigo sobre SOLID vamos falar sobre o Princípio de Substituição de Liskov (LSP). Esse princípio leva o nome de Barbara Liskov, professora no MIT (Instituto de Tecnologia de Massachusetts), uma das primeiras mulheres a ter um doutorado em ciências da computação nos Estados Unidos e identificou a necessidade do LSP.

Liskov dava a seguinte definição para o Princípio de Substituição:

Let q(x) be a property provable about objects x of type T. Then q(y) should be provable of objects y of type S, where S is a subtype of T.

Um resumo da afirmação de Liskov seria que subclasses devem ser substituíveis por suas superclasses.

No básico de programação orientado a objetos, aprendemos para realizar uma herança devemos pensar se um Objeto A é um Objeto B. Por exemplo, se uma Águia é um Pássaro, se um Labrador é um Cachorro, se um Quadrado é um Retângulo então podemos aplicar essas heranças porque elas fazem sentido.

LSP diz que o relacionamento “é um” não é suficiente e deve ser substituído por “é substituível por”. No problema clássico Quadrado-Retângulo: um retângulo tem quatro lados e quatro ângulos retos. Um quadrado tem quatro lados iguais e quatro ângulos retos. Na geometria todos os quadrados são retângulos, então para todo quadrado é correto dizer que ele é um retângulo. Vamos ver um exemplo de código que exemplifica o problema Quadrado-Retângulo. Lembrando que os exemplos aqui estão escritos em C#, mas podem ser reproduzidos em qualquer linguagem orientada a objetos:

Temos um classe Rectangle com duas propriedades: altura e largura. Uma classe Square que herda de Rectangle e, como sabemos da geometria, um quadrado tem altura e largura iguais, estamos fazendo isso no nosso código cada vez que a altura ou a largura são modificadas. O método CalculateArea realiza o cálculo da área do retângulo.

O problema:

Ao executarmos esse código esperamos como saída o resultado de 4x5 que é 20, mas recebemos o resultado de 25. Nesse exemplo pode ser óbvio que isso ia acontecer, pois estamos instanciando um quadrado. Porém, numa aplicação maior, onde podemos receber um quadrado como parâmetro em diversos lugares, isso pode passar despercebido.

E quando dizemos que a altura é igual a 5, o nosso código seta a largura também para 5, resultando numa área de 25. O que ocorreu nesse caso foi que, de acordo com a geometria, o quadrado deve ter seus lados iguais, mas o retângulo não. Quando um quadrado herda de um retângulo nós temos um comportamento inesperado, que viola o Princípio de Substituição de Liskov porque um quadrado não pode ser substituído por um retângulo sempre que um retângulo é usado.

Para solucionar esse problema, vamos criar uma classe abstrata Shape que fará o cálculo da área onde Square e Rectangle irão herdar:

As classes Rectangle e Square ficariam assim:

Note que na classe Square estamos fazendo uma validação para garantir que a altura e largura sejam iguais. Sendo assim, o cálculo da área vai funcionar corretamente tanto para um retângulo quanto para um quadrado.

Na maioria das aplicações de negócio não lidamos com formas geométricas. Então como podemos detectar violações do LSP no nosso código?

  • Se estamos validando o tipo do objeto usando as palavras chaves is ou as num código que deve ser polimórfico:
  • Se precisamos validar quando o objeto é nulo:
  • Ocorrências de NotImplementedException:

Se você começar a notar muitas ocorrências dos problemas citados acima no seu código, reavalie suas heranças e interfaces, se elas realmente fazem sentido. Você também pode encontrar problemas inesperados na execução da sua aplicação ou muito código duplicado.

Chegamos ao fim desse artigo sobre LSP. No próximo, vamos falar sobre o Princípio da Segregação de Interfaces. Até lá.

Esses exemplos estão disponíveis no meu GitHub.

Links para todos os artigos sobre SOLID:

Princípio da Responsabilidade Única (SRP)

Princípio Aberto Fechado (OCP)

Princípio da Segregação de Interface (ISP)

Princípio da Inversão de Dependência (DIP)

--

--