Não herdar é libertador

Publicado: 23 de setembro de 2013 em Uncategorized
Tags:

Quando eu me iniciei no mundo da Programação Orientada a Objetos eu ficava muito fascinado com a possibilidade de herdar classes e procurava oportunidades de fazer isso. Sempre lembrava que pessoa herdava de mamífero que herdava de animal, do qual herdava também o pássaro, do qual herdavam a galinha e o gavião, sendo que mamífero emite som e cada classe concreta implementa o seu próprio som.

Menos a girafa, que se você invocar o método “emitir som” decerto é lançada uma “not implemented exception” ou a girafa se comporta de uma maneira muito louca e você vai ter que depurar pra entender.

Mais complexo ainda é o momento em que você descobre que galinha e gavião também emitem som e resolve subir o método abstrato “emitir som” da classe Mamífero para a classe Animal. E daí emitir som aparece na classe minhoca a qual, mesmo que emita algum som, não é relevante para o seu negócio.

Há alguns anos comecei a perceber que herdar estava mais me atrapalhando que ajudando. Não digo que herdar não serve para nada. O que eu sugiro é não pensar na herança como primeira alternativa para a solução do problema. Ao pensar em situações onde usei herança no passado, agora quase sempre me ocorre que o problema seria melhor resolvido de outra forma mais simples. Herdar pode parecer a solução mais fácil no começo mas logo você sente as dores da escolha.

Neste texto eu tento desmistificar alguns equívocos comuns ao pensar em herança e descrevo alguns problemas que podemos criar ao usá-la.

Herança não é reutilização

Herdar uma classe para reutilizar um recurso dela ou desenhar uma herança ao perceber que um recurso será necessário em todas as classes daquela natureza, é ótimo exemplo de mal uso de herança, e talvez o engano mais comum.

Reutilizar é escrever código uma única vez de maneira tão coesa e desacoplada que ele possa ser usado novamente todas as vezes que houver a mesma necessidade.

A forma mais simples de reutilizar é encapsular o código em uma nova classe e consumir esta nova classe sempre que precisar.

Herança não é necessária ao polimorfismo

Uma classe pode implementar diversas interfaces e assim ser polimórfica.

Uma classe pode implementar interfaces hierarquicamente desenhadas e, assim, ser hierarquicamente polimórfica.

Interfaces herdando interfaces podem contribuir para um bom design, dispensando a quebra de abstração e o acoplamento trazido pela a herança de classes. Mas essa prática ainda é um risco para o aumento acidental da complexidade e exige muito mais esforço para ser responsavelmente projetada.

Herança aumenta o acoplamento

Eu posso escrever todo o código da minha classe completamente desacoplado, recebendo por parâmetro ou por alguma outra técnica de injeção tudo que eu precisar, mas se herdei, estou preso à classe pai e o código dela terá que ir comigo, com todas as suas dependências, para toda parte. Isso pode dificultar os testes unitários da minha classe filha ou a sua reutilização em outros contextos.

Herança quebra a abstração

Quando me acoplo, e ao herdar eu estou me acoplando à classe pai, todas as limitações do artefato a que me acoplei serão agora minhas limitações também. Algumas vezes eu serei chamado a investigar as dependências da classe que herdei para entender quais são as limitações e acoplamentos que herdei junto.

Os problemas pioram quando eu decido sobrescrever um método não abstrato (um método que já possui implementação na classe pai). Ao fazer isso eu preciso decidir se chamo ou não o método pai, e se faço isso antes ou depois do meu próprio código. Vou precisar entender muito bem como funciona o método na classe pai e lá se foi a abstração.

Herança aumenta a complexidade

Quem abusa do uso de herança acaba encontrando em uma classe um método que não deveria estar lá – um método que além de não ser coerente ao contexto (o que causa confusão), se for invocado ou nada acontece, ou lança uma exceção de “recurso não implementado”, ou causa um erro inesperado devido ao estado inválido do objeto para o uso daquele método. Um design envolvendo herança e que não tenha sido muito bem projetado (e às vezes mesmo tendo sido muito bem projetado) acaba levando a esta situação onde um comportamento precisou ser adicionado e não havia um local ótimo para ele na hierarquia, então ele acabou entrando onde era necessário e acabou aparecendo também onde não era. Foi adicionada portanto uma complexidade não inerente ao negócio do aplicativo – uma complexidade acidental.

Ou não

É claro que você pode herdar, unir realmente polimorfismo e reutilização numa hierarquia e ainda assim não sofrer de alto acoplamento nem quebra de abstração nem aumento acidental de complexidade. Neste caso, é possível que você tenha atingido tal nível de maturidade que já percebeu que não precisa dessa herança.

Nos últimos tempos, toda vez que me ocorre uma herança para resolver um problema, eu procuro evoluir o design para não precisar desta herança. Quando consigo, me deparo com um código muito mais simples e claro, coerente e reutilizável, e me sinto bem por não ter herdado. E daí o título deste texto.

Seria isso.

Anúncios

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s