Possibilidades de design no uso do seu Generic DAO
Muitas vezes, quando estamos criando nosso sistema temos a tentação de criar o GenericDAO para não ter que ficar repetindo as operações CRUD e listagens.
O maior problema com o GenericDAO
é que não necessariamente todas as operações fazem sentido para uma determinada classe. Daí o que fazer se, por exemplo, não faz sentido excluir um pagamento?
public class PagamentoDAO extends GenericDAO<Pagamento> { @Override public void excluir(Pagamento pagamento) { throw new UnsupportedOperationException(); } }
Não parece uma solução muito elegante, mas é um dos únicos jeitos de proibir uma operação declarada na classe mãe, e ainda assim, só funciona em tempo de execução. Esse é um dos principais motivos para muitos não gostarem de usar o GenericDAO
e preferirem usar composição ao invés de herança. Mas como fazer para não repetir o código trivial das operações do CRUD?
Um dos jeitos é usar uma outra abstração de persistência de objetos: o Repository. Com o Repository, temos um lugar onde podemos guardar e buscar por objetos, não importando como fazemos isso. O DAO já está muito ligado com armazenamento em banco de dados, e foi criado quando as operações do BD eram muito trabalhosas (em especial no JDBC).
E como juntar o Repository com o GenericDAO
? O Repository pode ser definido, por exemplo, como uma interface, e aí podemos fazer o seguinte: se, para um pagamento, faz sentido apenas salvar e listar, mas não excluir, então criamos a interface:
public interface PagamentoRepository { void salva(Pagamento pagamento); Pagamento busca(Long id); List<Pagamento> lista(); }
E usamos o GenericDAO
como implementação dessa interface:
class PagamentoDAO extends GenericDAO<Pagamento> implements PagamentoRepository { // implementacao extra }
E no nosso código de domínio "nunca" referenciaremos o PagamentoDAO
, apenas o PagamentoRepository
, assim mesmo que a implementação saiba fazer mais coisas, a interface só expõe as operações suportadas.
Se você usa Injeção de Dependências e algum framework que a suporta (como o VRaptor, Spring ou Java EE6), você pode deixar os _DAO_s apenas como infraestrutura, e usar os Repositories como interfaces públicas da sua aplicação:
public class PagamentoController { public PagamentoController(PagamentoRepository repository) { this.repository = repository; }
public void salva(Pagamento pagamento) { // validações e outras regras repository.salva(pagamento); } }
Ainda poderíamos melhorar o nome do nosso repositório para BaseDePagamentos
, ContasAPagar
ou ainda Pagamentos
, evitando usar sufixos nas classes. Usando abstrações e padrões simples conseguimos evitar repetição de código sem perder a semântica e restrições das nossas classes de modelo, além de esconder detalhes de implementação.