quarta-feira, 6 de abril de 2011

Herança vs Composição

Gostaria de compartilhar o artigo publicado no site iMasters pelo colega Lucio Camilo, fazendo um comparativo interessante entre Herança e Composição.

Na verdade, esse artigo não é bem um comparativo, e sim uma "desqualificação" do uso da herança. Na minha opinião, não há solução ideal para todos os problemas, nem modelo ideal de arquitetura. Há sim uma solução específica para um problema específico.

Citando o velho exemplo do objeto Veículo, que pode ser especializado como Carro, Bicicleta, Carroça, Caminhão, é um caso claro onde a Herança se aplica muito bem. Mas volto a dizer, não sou defensor de arquitetura A, B ou C, defendo sim a avaliação de cada problema, e a aplicação da solução adequada para cada um, seja com Herança, Composição, Agregação, ou seja lá o que for.

Vamos ao artigo publicado:
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.
Fonte: iMasters

Nenhum comentário:

Postar um comentário