Você está na página 1de 3

Artigo extraido do Blog da Caelum

http://blog.caelum.com.br/2006/10/14/como-nao-aprender-orientacao-a-objetos-heranca/

Como no aprender orientao a objetos: Herana


Por Paulo Silveira em 14/10/06

J falei sobre os problemas que os getters e setters podem trazer, caso usados de maneira indiscriminada. A bola da vez a herana. Lembro quando comecei a programar profissionalmente em Java, no final de 2000. Estava em um projeto juntamente com o Tiago Silveira, e algo que eu adorava fazer era que meus beans estendessem de HashMap quando tivessem propriedades dinmicas. O Tiago vivia me criticando, dizendo que eu deveria ter um HashMap e no ser um HashMap. Eu no entendi o porqu de ter muito mais trabalho para escrever mtodos que apenas delegariam, em vez de j herdar tudo! To simples, e herana parecia se encaixar to bem nesse caso. Depois de um tempo comecei a sofrer bastante: tipos errados eram passados como argumento, minha classe possuia mtodos que eu no queria e tambm nao era para trat-la como um mapa. Seria o eu o nico a cometer essa atrocidade apenas para economizar um pouquinho de cdigo? E a resposta vem do java 1.0:
class Stack extends Vector class Properties extends Hashtable

Claramente um Properties no deveria ser um Hashtable, afinal ela no mapeia objetos a objetos, mas acaba nos fornecendo um mtodo put(Object, Object), que se usarmos sem passar Strings vai causar problemas. Como resolver isso depois que j nos comprometemos com a herana? A nica soluo colocar um aviso grande no javadoc para ningum usar determinados mtodos herdados! O mesmo vale para a Stack e o Vector. Uma Stack no um Vector, definitivamente. Se voc pensar direitinho, uma pilha tem comportamento oposto da implementao da Vector! Se um gato possui raa e patas, e um cachorro possui raa, patas e tipoDoPelo, logo Cachorro extends Gato? Pode parecer engraado, mas o mesmo caso que os anteriores: herana por preguia, por comodismo, por que vai dar uma ajudinha. A relao um no se encaixa aqui, e vai nos gerar problemas.

Em vrios itens do livro Effective Java, Joshua Bloch cita o uso de heraa e de membros package-default e protected. O item 12 diz minimize o acesso a suas classes e membros, o item 15 diz desenhe suas classes pensando no uso de herana, caso contrrio proba-a e o item 16 prefira interfaces a classes abstratas. Mas sem dvida o principal o item 14: prefira composio em vez de herana, onde Joshua Bloch diz com todas as letras que herana quebra o encapsulamento. E isso no novidade, essa concluso atribuda a Alan Snyder, no artigo entitulado Encapsulation and Inheritance in Object-Oriented Programming Languages, que data de 1986! Faz incrveis 20 anos que algum percebeu que na maioria das linguagens a introduo da herana compromete seriamente os benefcios do encapsulamento (traduzido livremente do abstract do artigo citado). Se voc preferir uma opinio mais atual, pode ler esse post do Martin Fowler, que tem menos de um ms. No livro, Joshua Bloch da como exemplo a criao de uma classe filha de HashSet, e mostra que, sem conhecer profundamente o cdigo fonte da classe me, fica impossvel de que essa classe filha funcione corretamente ao reescrever um mtodo que aparentemente inofensivo. A partir do momento que voc precisa conhecer o cdigo fonte da sua me, voc quebrou o encapsulamento. Mais ainda: quando a classe me precisar sofrer alguma modificao, o desenvolvedor precisa estar ciente que pode quebrar o funcionamento de vrias classes filhas, que pressupunham determinado comportamento interno. Leitura recomendadssima. Existem outros exemplos mais sutis de mau uso de herana. Um deles quando criamos um DAO que me de todos os DAOs. Nesse meu post sobre DAO genrico o Fbio Kung fez um bom comentrio sobre isso, onde usamos herana apenas com o intuito de economizar meia dzia de linhas, sendo que a relao um no existe. O outro exemplo HttpServlet e sua prpria Servlet. Se voc j estendeu de HttpServlet sentiu o problema do mtodo service. Acontece que a implementao padro do mtodo service em HttpServlet chama o mtodo doGet, doPost, doPut, etc de acordo com o mtodo http utilizado. Esses mtodos possuem uma implementao padro que lanam uma exception. Se voc sobrescreve o mtodo service de uma serlvet , voc no pode chamar o super.service, pois caso o chame, ele chamar o do correspondente, jogando uma exception. Portanto jamais chame super.service. J no caso dos mtodos init(ServletConfig) e destroy, por exemplo, voc deve chamar o super pois no sabemos se a sua classe pai tem algum recurso a ser iniciado e/ou liberado. Sendo assim, em alguns casos voc deve, e em outros no deve chamar o super, e voc s vai saber disso quando conhecer o cdigo fonte de sua classe pai HttpServlet (ou ler a

documentao sobre esse comportamento estranho), quebrando o princpio de encapsulamento. fcil de reconhecer classes que apresentam esse mesmo problema: o javadoc delas costuma detalhar bastante como o mtodo funciona, e muitas vezes at mostrar o cdigo fonte deles!! Procure-as no jdk, elas no so poucas. Quem conhece um pouco a API de servlets sabe que eles tentaram resolver o problema adicionando um outro mtodo init, que no recebe parmetro, e esse sim deve ser reescrito e no precisamos chamar o super. Em outras palavras, uma gambiarra. Aqui cabe lembrar o que o Joshua Bloch falou: Crie suas classes pensando em herana, ou ento proiba-a. Parece que HttpServlet no cai nesse caso Minha proposta para a HttpServlet seria bem simples: voc poderia implementar alguma interface, digamos PostProcessor, GetProcessor, entre outras, e registra-la na HttpServlet (um mtodo setPostProcessor, ou passando para o construtor, ou ainda no XML). A interface PostProcessor poderia ser parecida com:
public interface PostProcessor { void init(ServletConfig config); void doPost(HttpServletRequest request, HttpServletResponse response); void destroy(); }

Se sua classe quisesse aceitar tanto GET quanto POST, bastaria implementar ambas interfaces. Interfaces! O caminho desse lado. Repare que sempre podemos substituir herana atravs de um refatoramento simples: extrao de uma interface com os mtodos herdados, somado a criao de uma implementao que simplesmente delega esses mtodos para a antiga classe me. Quando ento usar herana? Essa uma questo difcil. Na minha viso particular, a resposta seria um enftico "quase nunca". Creio que a resposta aqui fique um pouco a critrio de cada desenvolvedor, mas sempre com muita cautela!

Você também pode gostar