Um tempo atrás eu falei sobre o princípio de Liskov (LSP), e perguntei se uma classe quadrado deveria herdar de uma classe retângulo. Se você não leu o post ou não conhece o princípio de Liskov, sugiro dar uma lida antes de continuar neste post.

Eu entendia o significado do princípio, mas não havia absorvido claramente como ele funcionava. O princípio de Liskov já foi declarado de diversas formas. Ele diz, resumidamente (e livremente):

Classes base devem poder ser substituíveis por suas classes derivadas

Ou ainda:

O cliente de uma classe deve ser capaz de usar suas derivadas sem se preocupar com isso

Formalmente, segundo Barbara Liskov, autora do princípio, ele diz o seguinte:

“If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T.”

Ou, em português:

“Se para cada objeto o1 de tipo S existe um objeto o2 do tipo T sendo que para todos os programas P definidos em termos de T, o comportamento de P não é alterado quando o1 é substituído por o2 então S é um subtipo de T.”

A declaração formal, apesar de menos simples, é muito mais clara.

A beleza que encontrei enquanto estudava o princípio é sua relação com design by contract. Quando desenvolvemos com um contrato em mente, falamos sempre de pré-condições e pós-condições. E é aí que o princípio de Liskov fica realmente claro. O LSP é muito mais claro se definirmos ele em termos de contrato. Fica assim:

Uma classe derivada deve sempre ter em cada um de seus métodos pré-condições mais flexíveis, e pós condições mais restritas, em relação à sua classe base.

Voltando ao problema do quadrado e do retângulo. Porque um quadrado não pode herdar de um retângulo? Porque as pós condições da alteração de um lado de um retângulo são mais restritas que as pós condições do mesmo procedimento quando realizado com um quadrado. Um retângulo define que, ao mudar um lado, o outro permanece inalterado, enquanto que no quadrado essa condição é relaxada. O contrário também é verdade, o retângulo não pode herdar de quadrado, porque as pré-condições do quadrado são mais restritas que a do retângulo: os lados tem que ser sempre iguais.

A grande questão nessa discussão toda é que a relação “é um”, que costuma definar a herança, é apenas parcialmente útil. Herança, em orientação a objetos, diz respeito à comportamento, e não a conceitos do mundo real, como a geometria. No mundo real, um quadrado é um retângulo. Em OO não é, porque o comportamento esperado de um retângulo (mudar os lados independentemente) é diferente do esperado de um retângulo (os lados são sempre iguais).

Tenha isso em mente: herança tem a ver com reutilização de comportamento.

Giovanni Bassi

Arquiteto e desenvolvedor, agilista, escalador, provocador. É fundador e CSA da Lambda3. Programa porque gosta. Acredita que pessoas autogerenciadas funcionam melhor e por acreditar que heterarquia é mais eficiente que hierarquia. Foi reconhecido Microsoft MVP há mais de dez anos, dos mais de vinte que atua no mercado. Já palestrou sobre .NET, Rust, microsserviços, JavaScript, TypeScript, Ruby, Node.js, Frontend e Backend, Agile, etc, no Brasil, e no exterior. Liderou grupos de usuários em assuntos como arquitetura de software, Docker, e .NET.