[quote=tnaires]
Um exemplo que vemos com freqüência aqui no GUJ - e que acredito que você enquadra como "generalização mal feita", assim como eu - está ilustrado abaixo:
Veja que o esforço de manutenção de uma camada de persistência que foi implementada como acima é maior do que se tivéssemos simplesmente:
Sim, realmente eu vejo isso como uma generalização mal feita. Por alguma razão as pessoas adoram criar classes chamadas DAO e as nomeiam assim mesmo quando sabem que não são DAOs. Isso, para mim, é surreal. O Sindrome de DAO é um problema grave. É a gripe suina do design Java do pós EJB 2.1.
Este problema é diretamente derivado de um desrespeito ao SoC. Qual seria a diferença entre a responsabilidade do DAO e do Repositório ? A resposta comum é que o rep é para colocar coisas do dominio e o dao para coisas de infra. Mas isso nunca acontece. Se vc usar hibernate por baixo do dao e criar criterias no repositorio o dao só serve para passar a bola à frente. Não ha desacoplamento algum já que o DAO não tem qualquer responsabilidade. Se o rep criasse critérios agnosticos (que não dependem de nehuma lib) e o dao os interpretasse para criteria do hibernate, perfeito. Assim teriamos 2 responsabilidades diferentes e podemos até pensar em subsittuir o HibernateDAO por outro usando outra tencologia já que ele é um interpretador. hum… mas perai! o DAO é suposto ser um datamapper. Esse papel de interpretador não cabe em um dao. Logo esse objecto intermédio não é um dao, é um Interpreter que atua como estratégia para o repositorio. Veja que o problema não é o numero de camadas, mas a responsabilidade de cada uma. E que o verdadeiro datamapper está algures dentro do próprio hibernate.
A estrutura seria assim:
(quando vc é tentando a colocar Impl no nome da classe isso significa que vc não sabe diferenciar em quê a implementação é diferente de outra)
ProdutoRepository não precisa implementar nenhuma interface especifica chamada Repository. Contudo, ele pode. Mas aqui não estamos usando o mesmo padrão que no DAO (interface + N implementações) que é um tipo Service. Estamos usando outro padrão : Layer Super Type.
A diferença não está na implementação porque ambos usam interface + N implementações. A diferença está na responsabilidade e na abstração.
O Service tem uma interface porque define um contrato. As implementações são especificações reais de como esses contratos podem ser providos. A implementação é irrelevante para quem usa o contrato.Todas as implementações têm a mesma responsabilidade (satisfazer o contrato) e o mesmo comportamento ( dado pela interface do contrato)
O Layer Super Type tb tem uma interface, mas esta não é um contrato do qual derivam implementações. É um denominador comum entre objetos com responsabilidades diferentes que se comportam de forma semelhante. ProdutoRepository e ClienteRepository tem responsabilidades diferentes mas compatilham alguns comportamentos semelhantes como adicionar, remover e procurarTodos. Eles tem alguns comportamentos iguais, não todos. Por exemplo, produtorepo tem findProdutoComNecessidadeDeReposição que não faz nenhum sentido para ClienteRepositorio
Não consigo sublinhar o suficiente quando é importante entender os padrões. A maior parte das pessoas não os entende e isso causa problemas - alguns absurdos. Contudo, se entender SoC e seguir uma ou duas práticas não é necessário saber padrões. Pior que não entender é dizer que não é necessário e causa problemas. Ao contrário, padrões são coisas boas. Eles ajudam a criar aplicações de forma mais rápida e com menos custo e esforço de manutenção. Eles são o “caminho das pedras” , a “receita de bolo” , de fato. Sem gambiarra.
Se isto não fosse verdade não existiram livros como “Refactoring to Patterns”.