Bom dia galera, acho que muitos ja conhecem esse artigo ou algo relaciona aos exemplos citado nesse artigo. http://hibernate.org/328.html
Muita gente acha que o uso de DAO+Hibernate é desnecessário e houve algumas discussões aqui no forum a alguns meses atraz, porem nao sei se chagou a uma solução ou se existe uma abordagem melhor de se trabalhar com hibernate sem usar DAOs.
Essa semana tava lendo um artigo na Caelum que tb trata sobre o mesmo assunto de DAO+Hibernate, e tava lendo o seguinte comentario.
Porque mau uso de herança ?
Como aplicar polimorfismo nesses DAOs ?
Todo esse mal estar com herança começou qdo grandes caras da OO, falaram que pessoas estavam usando-a de qualquer jeito e que isso fazia com que ela perdece o sentido!
Mas herança bem utilizada lhe traz muitos beneficios! Não podemos medir o mal uso da herança so atraves do não uso do polimorfismo.
Na API do Java está lotado de maus exemplos tais como as classes de IO (antes do java.nio), servlets, stack e vector como o Paulo chamou a atenção no blog da Calum. Aliás o problema dos servlets se propaga em código mal cheiroso por vários framemeworks a começar pelo tão usado Struts.
E o livro do Coad que dedica todo o capítulo 2 enfatizando que se deve usar composição ao invés de heranças é de 1999. Portanto quem começou a programar em Java depois disto não tem muitas desculpas para insistir.
O proprio exemplo de DAO+Hibernate citado no site da hibernate nao seria um bom exemplo de herança !? Mesmo porque nao vejo como aplicar polimorfismo neste caso, e so porque nao foi aplicado polimorfismo nao creio que ja classifica como mal uso de herança.
Sem tempo para me alongar só vou chamar a atenção que acho que há um pouco de confusão aqui. Poliformismo às vezes é mais difícil de obter quando se programa por interfaces. Com herança é automático.
Mas falta de poliformismo não prejudica em nada o projeto de uma classe ou interface porque simplesmente pode não ser necessário.
Tb acho que existe um pouco de confusao aqui, por parte minha tb. Lendo o trecho abaixo retirado do wikipedia procurei entender um pouco mais de Polimorfismo
Portando analizando o codigo abaixo:
public class GenericHibernateDAO<T, ID extends Serializable, DAOImpl extends GenericDAO<T, ID>>
implements GenericDAO<T, ID> {
Verificamos que GenericHibernateDAO implementa a GenericDAO e escreve as funcoes conforme a necessidade do hibernate. Isso seria um exemplo de polimorfimos ja que eu poderia ter outra classe GenericJDBCDAO que tb implementa GenericDAO e escrever as funcoes conforme a necessidade do JDBC.
Despois nos temos o seguinte codigo:
public class HibernateUsersDAO extends GenericHibernateDAO<Users, Long, UsersDAO>
implements UsersDAO {
Aqui apesar de HibernateUsersDAO implementar uma interface, creio que essa opçao seja justamente para que o *Cliente nao saiba qual a forma de conexao com o banco, pois a **Factory fica responsavel em retornar os DAOs para o Cliente.
O Fato de HibernateUsersDAO extender a classe GenericHibernateDAO é justamente para herdar suas funcoes e nao teria como aplicar polimorfismo aqui.
Portanto nao acho que isso seja um exemplo de mau uso de herança.
*Cliente: Entendo que este fica na camada de controler, trafegando informacoes entre a camada de persistencia e a camada de apresentacao
**Factory: Responsavel por criar os DAOs, mais detalhes no artigo da pagina do hibernate
Sei que vc conhece afundo a API, mas poderia citar pra gente um exemplo mais usual de mal uso de herança, pois até o momento nao identifiquei e creio que em algum momento eu mesmo possa estar cometendo tal erro.
Um monte de classes descendem de InputStream que é abstrata mas poderia ser uma interface. Você poderia fazer sua classe implementando a interface com os métodos básicos em uma classe helper que você instanciaria na sua classe fazendo composição.
Veja java.nio.channel que eles usam um monte de interfaces.
Imagine como a API de servlets seria melhor se eles tivessem se baseado mais em interfaces. Quando se escreve uma API, é mais importante ainda projetar baseado em interface.
Para desenvolver por interfaces é preciso mudar um pouco o modo de desenvolver. Ao invés de criar uma classe abstrata e sair extendendo, o melhor é projetar tudo como interfaces e depois refatorar. Em geral fazer o caminho inverso de herança para interface é mais trabalhoso (a menos quando for fácil usar o pattern de refatoração “Substituir herança por delegação” do Fowler).
Para ver as vantagens de desenvolver baseado em interfaces leia o Effective Java. É um livro fundamental para todo desenvolvedor.
Poderia mas nao é obrigatorio… um exemplo disso seria a propria HibernateDAOFactory que extende a DAOFactory.
A DAOFactory é uma classe abstrata que poderia ser uma interface, mas ae como ficaria a funcao getDAOFactory ?
Prefiro deixar como abstrata, parece mais simples. o que seria composicao ?
O que seria esse refatorar
?
Daria para fazer isso com os exemplos de DAOs acima ? ou qualquer outro. Como ?
Ele é um resumo de parte do capítulo 2 Design with Composition rather than Inheritance do livro Java Design do Peter Coad de 1999 que eu citei antes. Ele cria um checklist sobre quando é possível usar herança:
A sub classe É um tipo especial de e não Faz papel de
Um objeto jamais será um objeto de outra classe
A sub classe estende as responsabilidades da super classe ao invés de substituir
A sub classe não estende a capacidade do que é meramente uma classe utilitária (que tem uma funcionalidade boa de ser usada)
Para super classes do domínio do problema, a subclasse expressa tipos especiais de papeis, transações ou dispositivos
Atualmente acredito que mesmo em alguns casos destes em que ele admitia herança, pode haver vantagens desenvolvendo para interfaces.
Seria possível refatorar toda a API criando outras interfaces e algumas classes concretas que chamassem os métodos nativos de IO. O pacote java.nio.channels usa um modelo baseado em interfaces.
Só para exemplificar vou brincar de Copy&Paste:
Para substituir apenas um decorator (BufferedInputStream ou um outro qualquer) por um CustomInputStream se pode fazer tal como abaixo no exemplo copiado do livro Pragmatic Interface Oriented Design do Ken Pugh (não é um livro excelente mas é fácil de ler).
public interface InputStream {
public int available() throws IOException;
public void close()throws IOException;
public void mark(int readlimit);
public boolean markSupported();
public int read()throws IOException;
public int read(byte [] bytes) throws IOException;
public int read(byte [] bytes, int offset, int length)
throws IOException;
public void reset() throws IOException;
public long skip(long n) throws IOException;
}
Para fazer as funções básicas que queremos substituir se usa uma classe helper que pode chamar os tais métodos ou usar alguma implementação default
public class InputStreamHelper {
private InputStream inputStream;
public InputStreamHelper(InputStream input) {
inputStream = input;
}
public int read(byte [] bytes) throws IOException
{
read(bytes, 0, byte.length);
}
public int read(byte [] bytes, int offset, int length) throws IOException;
{
// Lê os bites e os coloca em um array
}
public long skip(long n) throws IOException
{
// . . .
}
}
Por fim a tal classe que funciona como um dos decorators e que usa a helper como composição (ou delegação)
class CustomInputStream implements InputStream {
private InputStreamHelper inputHelper;
public CustomInputStream()
{
inputHelper = new InputStreamHelper(this);
}
public int available() throws IOException {...}
public void close()throws IOException {...}
public void mark(int readlimit) {...}
public boolean markSupported() {...}
public int read()throws IOException {...}
public int read(byte [] bytes) throws IOException
{
return inputHelper.read(bytes);
}
public int read(byte [] bytes, int offset, int length) throws IOException
{
return inputHelper.read(bytes, offset, length);
}
public void reset()throws IOException
public long skip (long n) throws IOException
{
return inputHelper.skip(n) ;
}
}
Não gosto de ser muito eXtremista! Utilizar de 8 ou 80 as vezes não é muito bom!
Continuo concordando que em alguns lugares há mau uso de herança como vc citou! Mas ainda acho que a herança ainda pode ser usada melhor que a composição em alguns lugares!
Ao meu ver a linguagem Java delegou muito mais poder ao uso de interfaces do que ao uso de herança, pelo fato que em java não podemos usar herança multipla.
Por muitas vezes por causa dessa restrição me vi criar uma interface INT, uma classe INTCONCRETA implementar INT e uma classe que seria uma sub-tipo de int implementar INT e ser composta por INTCONCRETA e delegar para INTCONCRETA pelo fato de eu não poder usar mais uma herança. Isso resulta em varias delegações para identicas para INTCONCRETA, um mau cheiro, acho que esse seria uma exemplo em que composição é pior que herança!
Obs.: Delegações identicas são aceitas para representar papéis, ou seja, quando o objeto que compõe pode variar. O que não acontece no caso acima pois minha classe “é um sub-tipo de” ou pode ser visto como “é um”
Entendi o que vc disse sobre composicção e o problema existente na API de servlet. Ainda nao li o livro Effective Java, mas o artigo da Caelum explica claramente o problema da API nos servlets cidado por vc.
Porem ainda nao enxerguei problema algum de mau uso de herança no artigo no site da hibernate.
Existem 5 regras para o uso de herança, os quais estao citadas abaixo, e nenhuma delas parece infringir o artigo da hibernate
1)O objeto “é um tipo especial de” e não "um papel assumido por"
2)O objeto nunca tem que mudar para outra classe
3)A subclasse estende a superclasse mas não faz override ou anulação de variáveis e/ou métodos
4)Não é uma subclasse de uma classe "utilitária"
5)Para classes do domínio do problema, a subclasse expressa tipos especiais de papeis, transações ou dispositivos
Segue abaixo um diagra de classes, sendo que fiz a refatoração usando composição ao inves de herança e nao vi vantagens.