Como definir a herança (revisando o princípio de Liskov)

Email

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.

Email
Esse post foi publicado em Sem categoria e marcado por Giovanni Bassi. Marcar link permanente.

Sobre Giovanni Bassi

Arquiteto e desenvolvedor, agilista, pai, filho, namorado, escalador, provocador.
Programa porque gosta, e começou a trabalhar com isso porque acha que trabalhar como administrador é meio chato. Por esse motivo sempre diz que nunca mais vai virar gerente de ninguém. E também porque acredita que pessoas autogerenciadas funcionam melhor e por acreditar que heterarquia é melhor que hierarquia. Mas isso é outro assunto.
Foi reconhecido  Microsoft MVP depois que alguém notou que ele não dormia a noite pra ficar escrevendo artigos, cuidando e participando do .Net Architects, gravando o podcast Tecnoretórica, escrevendo posts no blog e falando o que bem entende no twitter @giovannibassi. E por falar nisso é no twitter que conta pra todos que gerencia de projetos deve ser feita pelo time e não por um gerentes, que greves em TI são coisas sem sentido e que stored procedure com regras de negócio são malígnas.
Você já deve ter percebido (até porque está lá na primeira frase) que Giovanni é agilista. De tanto gostar disso ele trouxe os programas de certificação e treinamento  PSD e PSM da Scrum.org pro Brasil, e por causa deles, do MVP e de algum trabalho que aparece tem que ficar indo pros EUA de vez enquando, coisa que prefere não fazer. (É bem comum você ouvir ele perguntando porque a Scrum.org e a Microsoft não estão na Itália, por exemplo.)
Junto com alguns Jedis criou a Lambda3, que, apesar de ser pequena e de não ser muito comum no Brasil, insiste em fazer projetos e consultoria direito. Por causa da Lambda3 ele tem trabalhado mais do que quando era consultor independente, mas menos do que a maioria das pessoas. Quer dizer, isso se você considerar que os trabalhos junto à comunidade não são trabalho, caso contrário ele trabalha mais que a maioria das pessoas.
Recentemente ele resolveu que merecia viver melhor e ganhar uns anos de vida e desistiu de ser sedentário, fazendo algum barulho de vez em quando com os amigos no twitter com a hashtag #DotNetEmForma. Por causa do convite recente de amigos do lado Open Source (que ele respeita e admira), começou a escalar, e agora está sempre com as mãos machucadas. Mas ainda dá pra programar. Você encontra ele sempre em algum evento, como o TechEd, e o DNAD, mas também outros menos comuns para o pessoal do .NET, como a RubyConf. Nesses eventos, ou ele está vendo palestras, ou batendo papo com alguém, ou codando alguma aplicação que alguém achou que dava pra fazer durante o evento.
  • Marcelo M. Maciel

    Pequena correção na última linha :D

    Em OO não é, porque o comportamento esperado de um retângulo (mudar os lados independentemente) é diferente do esperado de um QUADRADO (os lados são sempre iguais).

  • Gabriel Araujo

    Se para cada CACHORRO do tipo CANIS_LUPUS_FAMILIARIS existe um GATO do tipo MAMIFERO sendo que para todos as SITUAÇÕES QUALQUER defindos em termos de MAMIFEROS, o comportamento da SITUACAO QUALQUER não é alterado quando o CACHORRO é substituído por um GATO, então CANIS_LUPUS_FAMILIARIS é um subtipo de MAMIFERO.

    eu entendi melhor quando eu li esse texto: http://letras.terra.com.br/dj-antoine/1190812/

    *humor

  • http://unplugged.giggio.net/ Giovanni Bassi

    Pois é Gabriel, essa é uma aplicação concreta da declaração formal.
    Eu acabei achando a descrição baseada em contratos mais útil…