Programação Orientada à Objetos — Quando usar herança?

Rafael Issao
Nov 2 · 5 min read

Nesta série de artigos sobre Programação Orientada à Objetos, gostaria de compartilhar a minha experiência em relação a este paradigma. Vou escrever sobre os conceitos, os quatro pilares, alguns princípios e irei discutir algumas descobertas que fiz ao longo da minha vida como desenvolvedor.

Vamos ao assunto deste artigo.

No artigo sobre Por que usar POO? cheguei a seguinte conclusão:

Herança não foi criada para reutilizar código.

Então para que serve? Quando devo utilizar herança?

Estudo e Análise

Pensei em como usamos herança para resolver alguns problemas que Programação Orientada à Objetos propõe resolver. Um dos problemas é código e lógica duplicada no projeto.

Eliminar código duplicado é possível se encapsularmos os dados e a lógica em uma classe. Assim podemos reaproveitar o código utilizando Delegation Pattern ou Composition.

Exemplo:

Exemplo de composição com a classe Stack

Logo, não precisamos de herança.

Pensei na herança para provocar polimorfismo. Foi assim que eliminamos alguns IFs perigosos no artigo anterior:

Polimorfismo utilizado no método imprimeNomeComPronomeTratamento

Mas a classe Autoridade poderia ser uma interface.

Mesmo polimorfismo, mas com interface

Ou seja,

herança pode provocar polimorfismo, mas para ter polimorfismo não precisamos de herança.

Estudando herança, aprendi um pouco mais sobre polimorfismo.

Aprendi que para provocar polimorfismo em:

  • Linguagens com tipagem estática, uma interface é suficiente. (Ex: Java, C#)
  • Linguagens com tipagem dinâmica, o duck typing é suficiente. (Ex: Ruby, Groovy, JavaScript)

Legal, aprendi bastante sobre polimorfismo. Mas sobre herança pouca coisa, pelo menos sei quando não utilizar.

"Aha! moment"

Depois de mais pesquisas, finalmente um Aha! moment aconteceu. Duas fontes me ajudaram a entender como usar herança:

O vídeo fala sobre os principais princípios de design quando codificamos e o curso ensina como programar em SmallTalk.

Recomendo muito o vídeo e o curso. Por favor, tirem um tempo para assistir e fazer o curso.

Mas o que os dois tem em comum é que eles falam muito sobre herança.

No vídeo, o mestre Venkat Subramaniam deu um exemplo de uma herança:

Faz sentido a herança. Um FogaoAGas é um Fogao, um FogaoEletrico é um Fogao e qualquer fogão consegue cozinhar.

Mas mesmo assim, nesse caso é melhor usar o Delagation Pattern ou Composição. Pois imagine a seguinte evolução das classes:

Nesse diagrama, o usuário precisa saber o tipo do objeto. Pois se não souber, não consegue cozinhar, como no exemplo abaixo:

Uso da hierarquia Fogao

Ou seja, para eu poder cozinhar, preciso saber o tipo do objeto para ligar o fogão. Voltou o if perigoso comentado no artigo anterior.

Mas e se a herança fosse assim:

No novo diagrama temos uma classe Chef que vai utilizar um objeto de uma das classes da hierarquia da classe Fogao.

Para a classe Chef, não importa o tipo do fogão, mas que consiga cozinhar. Cada tipo pode ter um desempenho diferente de temperatura, duração de cozimento e etc. Mas o Chef, com a experiência que tem, consegue cozinhar com qualquer um.

Em termos mais técnicos, para a classe Chef, não importa o tipo do fogão. O que importa é que ele consiga cozinhar. Ou seja, o comportamento de todos os objetos da hierarquia deve ser exatamente o mesmo.

Se um dia, quebrar o fogão que ele tem, ele pode usar um elétrico. Ou um fogão a gás. Ou seja,

Cada classe da hierarquia de Fogao seriam opções para a classe Chef

Liskov Substitution Principle

E assim entendemos melhor um dos princípios relacionados com herança, "Liskov Substitution Principle" (ou LSP), que diz:

  • Seja q(x) uma propriedade demonstrável dos objetos x de tipo T. Então q(y) deve ser verdadeiro para objetos y do tipo S onde S é um subtipo de T.

Simplificando, vamos separar em três definições:

  • Herança deve ser utilizado quando existe a necessidade da substituibilidade.
  • Qualquer objeto que utilize um objeto da classe base A, pode utilizar um objeto da classe derivada B sem saber a diferença.
  • Serviços da classe derivada deve exigir nada além e prometer nada menos que o serviço da classe base.

Ou seja, LSP (“Liskov Substitution Principle”) aplicado no exemplo seria:

  • Substituibilidade: O objeto da classe Chef pode utilizar um objeto do tipo Fogao, FogaoAGas ou FogaoEletrico
  • O objeto da classe Chef que utiliza um Fogao, deve ser capaz de utilizar um FogaoEletrico ou FogaoAGas sem saber o tipo de fogão que está utilizando.
  • Um FogaoEletrico e FogaoAGas não deve exigir nada além para ser instanciado, ou prometer nada menos que um Fogao.

Se você precisa quebrar este princípio por alguma razão, então deve ser evitado a utilização de herança.

Exemplos de quebra de princípio:

  • Substituibilidade: Pode quebrar se seu código precisa saber se o objeto é um tipo mais específico. (Ex: Verificando o tipo com instanceof FogaoEletrico)
  • Uma classe que aceita Fogao mas tem um comportamento alterado dependendo do tipo do fogão. (Um fogão a gás não cozinha pois não está na tomada)
  • FogaoEletrico precisa uma ação extra para ser ligado. (Exigiu além do que um Fogao).

Legal! Agora sei quando usar herança. Preciso lembrar do LSP e tentar não quebrar o princípio.

Mas pensando bem, é muito complicado você encontrar uma situação em que realmente podemos utilizar herança.

Manter o princípio LSP não é simples.

E qual a vantagem de não quebrá-lo? E se eu quebrar? Quais são as consequências?

Curso do Pharo MOOC?

Para entender as vantagens do LSP, primeiro precisamos entender bem o que são Classes, e um outro princípio relacionado, Open/Closed Principle (OCP).

O curso sobre Pharo MOOC me ajudou a compreender melhor herança e classes.

Mas este assunto merece um artigo a parte. Vamos falar mais sobre Classes primeiro.

O que são classes? O que são hierarquia de classes? O que eles representam?

Vou tentar responder estas perguntas no próximo artigo.

Até a próxima!

Thanks to Rosana Matos

Rafael Issao

Written by

"I'm a humble developer".toLinkedIn() == "Enthusiastic Agnostic FullStack Senior Agile Leader Developer"

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade