/Desenvolvimento

voltar
/Desenvolvimento

Herança X Composição

PorLúcio Camilo em

Há algum tempo, herança era considerada a ferramenta básica de extensão e reuso de funcionalidade. Qualquer linguagem que não possuísse o conceito de herança “não servia para nada”. Atualmente, todos os artigos sobre padrões de projeto desaconselham a utilização de herança.

E agora?

Um princípio básico de padrões de projeto é “dar prioridade à composição”, preferir sempre “tem-um” ao invés de “é-um”. Quanto à herança, deve ser utilizada com muita prudência e em pouquíssimas situações.

Uma das poucas certezas que temos no desenvolvimento de aplicações é que existirão alterações. Portanto, a utilização de herança para fins de reutilização não dá tão certo quando se tratam de manutenções nos códigos já existentes.

Supondo que utilizamos uma classe pai para encapsular o comportamento de algum objeto. Dessa forma, todas as nossas classes filhas herdarão esses comportamentos. E o problema ocorre quando alguma das classes filhas não precisam de algum dos comportamentos que estão encapsulados. Passamos a ter que mudar o código na classe filha para que o método que foi herdado funcione da maneira específica para esse objeto. Ou, pior ainda, quando esse objeto não necessitar desse comportamento, então teríamos que sobrescrever o método para que ele não faça nada.

Alguns dos problemas dessa implementação é que o encapsulamento entre classes e subclasses é fraco, e o acoplamento é forte. Assim, toda vez que uma superclasse for alterada, todas as subclasses podem ser afetadas. Perceba que estamos violando o princípio básico de OO (Alta Coesão, Baixo Acoplamento).

Ainda com herança, a estrutura está presa ao código, e não pode sofrer alterações facilmente em tempo de execução, fazendo diminuir a capacidade de polimorfismo.

Quando utilizamos composição, instanciamos a classe que desejamos dentro de nosso código. Dessa forma, estamos estendendo as responsabilidades pela delegação de trabalho a outros objetos. Em vez de codificar um comportamento estaticamente, definimos pequenos comportamentos padrão e usamos composição para definir comportamentos mais complexos. Ainda na utilização de composição, podemos mudar a associação entre classes em tempo de execução e permitir que um objeto assuma mais de um comportamento.

Ao utilizar a composição, teremos muito mais flexibilidade, além de ser mais comum em muitos padrões de projetos. Porém, na herança, temos uma possibilidade de desenvolver mais rápido, diminuindo o tempo de desenvolvimento, levando em conta que perdemos muito mais tempo mantendo e alterando o código original do que com o desenvolvimento inicial. Portanto, nosso esforço deve ser sempre voltado para a reutilização e para a manutenção.

Aproveito para indicar um artigo interessante que conta com códigos mostrando o motivo de nunca se utilizar herança: veja aqui.

Referências:
http://www.dsc.ufcg.edu.br/~jacques/cursos/map/html/pat/herancavscomposicao.htm
http://weblog.raganwald.com/2008/03/is-is-has.html
http://berniesumption.com/software/inheritance-is-evil-and-must-be-destroyed/

Deixe um comentário! 9

9 comentários

Comentários

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Comentando como Anônimo

  1. Ótimo artigo. Atualmente tenho vários projetos que vêm crescendo e no começo foram estruturados com herança.

    Como um velho ditado diz: “Tudo é demais, faz mal”.

    1. interface penso que não tem esses problemas e estão liberadas, é só uma declaração da api, não é herança. Não tem problemas de alto acoplamento, herdar comportamento indesejado, etc. É algo muito elegante e usado para reduzir o acoplamento entre classes na verdade.

      Classes abstratas é herança da mesma forma, mas muitas vezes é um uso legítimo. Inclusive é necessário para o design pattern “Strategy”. se você tem métodos abstratos e eles estão no lugar certo mesmo, seu design parece bom, então você vai usar de fato polimorfismo, isto é, são objetos que de fato tem o mesmo comportamento mas tem alguma especifidade para realizar aquela tarefa específica

  2. “nunca utilizar herança”. Acho que qualquer estratégia deve ser utilizada de acordo com a necessidade e com herança não ? diferente. Sei que muita gente exagera no uso de herança e coisa nao fica legal. Agora, dizer que nunca se deve usar herança é negar a própria natureza de liguagens OO. Herança é um conceito natural de objetos. Como exemplo podemos pegar a linguagem Java. Todos os objetos Java herdam de uma mesma classe. todos os tipos numéricos tem ancestrais comuns. Até enumeração tem herança. Então, será que só os engenhiros da sun podem usar herança, ou melhor só eles sabem usar herança. Eu acho que não. Em fim, nem tanto ao mar nem tanto a terra.

  3. acho que uma das coisas a se considerar é se vamos precisar ou não de polimorfismo, que a composição não tem. Há casos que queremos que um objeto se passe por outro, mas com um comportamento específico ao fazer determinadas tarefas. isso inclusive é usado em Design Patterns como Strategy. Claro, sempre se pode declarar uma interface para tudo que se queira, mas em alguns casos queremos passar um objeto para uma api que não fomos nós que escrevemos, de uma biblioteca por exemplo. Se você vai sobrescrever um método, não tem outro jeito.

    Outra coisa é pensar se toda a api antiga é importante na classe nova.

    Há casos que faz todo sentido sim herdar toda a api da classe pai. se vc tem uma classe FiguraGeometrica, com métodos como área, comprimento, esses métodos fazem todo sentido se for necessário criar uma classe Quadrado ou Circulo.

    Agora se você quiser representar uma forma como uma lista de pontos, é melhor usar composição, porque não queremos que nossa forma se comporte como uma lista

leia mais
Este projeto é mantido pelas empresas:
Este projeto é apoiado pelas empresas: