public void addAluno(Aluno aluno) {
alunos.add(aluno);
}
// outros metodos omitidos
public List getAlunosReprovadosNoAnoAnterior() {
AnoLetivo anoAnterior = new AnoLetivo(new Date().getYear() - 1);
return alunos.getAlunosReprovados(anoAnterior);
}
}[/code]
Gostaria de saber a opinião de vocês. Por exemplo: algo no DDD reprovaria essa idéia? É tecnicamente inviável? A dificuldade vale a pena? Estou agregando valor para o meu domínio?
- você vai ter que implementar todos os métodos de List. Eu diria que nunca você vai precisar disso tudo.
- você vai ficar tentado a implementar métodos que não façam nada só para obedecer o contrato.
- Geralmente as operações significativas do seu Repository, exceto adicionar e remover não estão no contrato da interface List.
- Outras pessoas que usarem esse repositório vão com toda vontade achando que faz tudo que uma List faz e irão se decepcionar.
- Caso você decida realmente implementar toda a interface (que seria o correto), imagine o trabalhinho para implementar alguns métodos como por exemplo iterator() e seus primos próximos?
- Quando você quiser usar os métodos que só estarão na implementação, terá que fazer uma chamada não-polimórfica.
Em fim, isso tudo se resume em uma coisa: Respeitar um contrato que para o cenário do Repository não faz muito sentido.
Apesar do conceito do Repository ser algo “como se fosse uma Collection”, seu contrato será diferente do da API Collections do Java, pelo menos IMO.
também pensei nestas possibilidades que você citou. Então, por enquanto, to pensando na possibilidade de respeitar o contrato feito com a interface. Por exemplo, esse código abaixo, apesar de não ser o ideal, já ajudaria:
[code]abstract class AlunoRepository extends ArrayList{
abstract List getAlunosReprovados(AnoLetivo anoLetivo);
} [/code]
A classe concreta por sua vez se encarrega apenas de implementar os métodos abstratos. Pode ser uma alternativa, não pode?
Continua com o mesmo problema pois você terá que sobrescrever todos os métodos. Caso contrário quem usar algum método que você não tenha sobrescrito obterá resultados não desejados.
Não é mais fácil você substituir essa herança por composição?
Sim sim! Por isso no primeiro exemplo usei interface, para dar liberdade pra nossa imaginação :). Usei herança neste exemplo por praticidade. Na prática, optaria com composição.
Pode ser, mas e se eu conseguisse uma solução onde eu não precisasse sobrescrever nenhum método da interface Collection/List?
Por exemplo, no caso de uma composição, como você sugeriu, eu sobrescrevo todos os métodos da interface List para delegar para um ArrayList. Ou seja, meu repositório continua sendo na íntegra uma Collection. Mas é aqui onde começam a surgir as dificuldade técnicas, ao meu ver. O hibernate talvez consiga numa boa identificar que foi adicionado um item na collection, já a implementação usando jdbc seria bem mais punk e pode exigir que não nos limitemos em apenas delegar tudo para ArrayList. Ficou claro o meu ponto de vista?
Talvez eu esteja exagerando. Mas o que estou fazendo é questionando o padrão Repository. Se a collection de alunos é um repositório ‘natural’ de alunos, por que ele não poderia aderir métodos específicos para acessar um centro de armazenamento de dados desacoplado? Ou seja, da forma que é implementado hoje você cria uma collection e um repositório. Essas duas coisas poderiam ser uma coisa só.
public void execute() {
turma = new Turma();
turmaRepository.add(turma);
aluno = new Aluno();
turma.addAluno(aluno);
}
ao invés de
public void execute() {
turma = new Turma();
turmaRepository.add(turma);
aluno = new Aluno();
turma.addAluno(aluno);
alunoRepository.add(aluno);
}
tudo bem que turma.addAluno() poderia adicionar aluno na collections e em seguida chamar alunoRepository.add, mas são duas chamadas com a mesma finalidade, não é?
andei pensando (ou viajando?) na possibilidade de juntar repository e collections.
Gostaria de saber a opinião de vocês. Por exemplo: algo no DDD reprovaria essa idéia? É tecnicamente inviável? A dificuldade vale a pena? Estou agregando valor para o meu domínio?
[/quote]
O padrão repository defini-se como "Acesso ao conjunto de instancias da entidade como se fosse uma coleção"
básicamente significa : “Acesso ao collection de X sem implementar Collection”
A ideia é que existam métodos como add e remove igual aos de Collection, mas que não tenham que existir métodos como toArray(), addAll() , retain(), etc…
Portanto, o proprio padrão está dizendo “Não faça repository implementar Collection”.
Por maioria de razão não faz sentido implementar List (os dados não são indexados por posição)
Em resumo: não vale a pena, não está agregando valor é mais dificil de implementar coerentemente e pior: vai contra o propósito do próprio padrão Repository.
Também não acho legal não, Thiago. Concordo com o Emerleite.
Sugestão?
/**
* Interface básica para repositório de objetos.
*
* @author XPTO
*
*/
public interface Repository<E extends EntityObject> {
/**
* Adiciona uma entidade ao repositório de objetos.
*
* @param entity
* @throws RepositoryException
*/
public void add(E entity) throws RepositoryException;
/**
* Atualiza uma entidade contida no repositório.
*
* @param entity
* @throws RepositoryException
*/
public void update(E entity) throws RepositoryException;
/**
* Remove uma entidade do repositório.
*
* @param entity
* @throws RepositoryException
*/
public void remove(E entity) throws RepositoryException;
/**
* Obtem um objeto da entidade contida no repositório, baseado no parametro recebido.
*
* @param id
* @return
* @throws RepositoryException
*/
public E get(Object id) throws RepositoryException;
}
E nas interfaces que extenderem esta, você adiciona métodos específicos de listarBlaBlaBla, etc, etc, etc. Ou então, implementa um get ou list com um search criteria da vida nesta própria interface básica, e boa.
É por isso que Repository é um Design Pattern . Existe um problema reccorente que é o fato dos objetos não serem auto persistidos. A melhor forma que se encontrou pra resolver isso foi essa. Tinhamos o DAO, que na verdade é um DataMapper misturado com Repository IMO. Achou-se interessante criar esse conceito de Repository para fazer parte do domínio.
Esse problema que você disse é justamente porque a collection não é o DB. O fato é que o os objetos não sendo auto-persistidos, o Repository se tornou uma alternativa elegante para termos algo no nosso Domain Model.
Quando a Collection, sinceramente não acho essa implementação vá ajudar. Mesmo que você crie um adapter não acho que vá ganhar muita coisa. Ainda mais se falarmos de sistemas web onde ao final da requisição os objetos morrem e na próxima vai ter que chamar tudo novamente.
No seu caso, vai adicionar o Aluno a turma e dificilmente após essa operação você vai querer uma lista dos alunos daquela turma. Talvez no próximo request você precise mas ai de fato seu objeto Turma já se perdeu. O máximo que você vai ter é o ID da turma armazenado no cliente de alguma forma e vai passar isso ao servidor pra começar tudo denovo.
Isso só acontece quando insistimos em colocar métodos de negócio dentro do DAO. Mas o DAO não foi feito pra isso.[/quote]
AHM??? Pode explicar isso? Quem disse isso?
Então fechado -> O padrão repository não é e nem pode ser uma Collection.
Tentarei continuar a discussão para essa abordagem que coloquei no início do tópico, mas consciente que Collections e Repository são coisas distintas.
[quote=sergiotaborda]O correto sempre será
turma = new Turma();
aluno = new Aluno();
turma.addAluno(aluno);
turmaRepository.add(turma);
Se a implementação especifica pode lidar com chamadas implicitas aos outros repositorios é outra historia.[/quote]
À partir desta colocação tentarei colocar minha insatisfação com repository: imagine o seguinte caso:
Turma turma = turmaRepository.get(turmaId);
Aluno aluno = new Aluno();
turma.addAluno(aluno);
Se por baixo dos panos é hibernate por exemplo, esse código funciona que uma beleza. Se for JDBC por debaixo dos panos o jeito mais fácil de garantir que aluno será persistido é usar alunoRepository.add(aluno);
Ou seja, você ainda não está completamente desamarrado da tecnologia de persistência. Mas enfim, não sou eu quem vai resolver este problema, hehe. O que acho desperdício é o fato de que em turma.addAluno eu já fiz uma chamada em alunos.add. Qualquer coisa fora disso, ao meu ver, é gambi para alinhar OO e Tabela Relacional.
A Collections no fim são usados no domínio. Uma solução não poderia ser deixar elas um pouco mais espertinhas e torná-las mais específicas para o negócio?
Enfim, o que me parece é que já não estou mais falando de repositório, mas de uma opção a ele. Uma coleção específica para o negócio. (humm… to viajando!)
Eu também não gosto muito da idéia por um outro ponto de vista: Por que é mesmo que você precisa se referenciar a um Repositório como List?
Para você satisfazer sua tentação, apenas faça como eu. Chame o repositório de Collection sem implementar java.util.Collection, mas não conta para ninguém!
no seu caso este seu repositório que você chama de collection também está servindo para definir que existe um relacionamento entre dois objetos, como no caso * Turma possui N Alunos*? Ou você ainda mantém uma collection demarcando a existência de um relacionamento?
no seu caso este seu repositório que você chama de collection também está servindo para definir que existe um relacionamento entre dois objetos, como no caso * Turma possui N Alunos*? Ou você ainda mantém uma collection demarcando a existência de um relacionamento?[/quote]
2a. opção. Mantenho a Collection, que para mim também é um repositório e eu não conto para ninguém.
Mas depende do caso. Naquela discussão de ter um repositório dentro de entidades ou não, se você escolhe por deixar repositórios dentro de entities, o repositorio acaba servindo para definir relacionamentos complicados como: “1 Turma possui N Alunos transferidos de outros estados”.
class Turma {
private final RepositorioDeAlunos rep;
...
public List<Aluno> getAlunosTransferidosDeOutrosEstados() {
return this.rep.getAlunosTransferidos(<de>, <para>);
}
}
O problema é que vcs continuam pensando no Repositorio como um outro nome para DAO.
Repositorio não é isso. O repositorio é o cara onde as logicas de mapeamento entre entidades ficam.
O codigo de repositorio de turmas é mais ou menos assim:
class RepositoryTurma (){
Collection turmas = new LinkedHashSet(); // aqui que fica a coleção ( pq um Set?)
RepositoryAluno repAluno;
public void addTurma(Turma turma){
this.turmas.add(turma);
for (Aluno aluno : turma){
repAluno.add(aluno);
}
}
}
Sabe quand vc faz um diagrama de classes e diz que Turma --contém–> Aluno ? Ora , o repositorio de turma conhece essa relação. Tão simples assim.
Não usei o DAO para reforçar o conceito de que Repositorio não é DAO. Não é.
Sergio, isso ajuda bastante se você não usa nenhum mecanismo de orm. Usando um orm, eu não acho que compense o esforço de fazer isso quando um simples @ManyToOne resolve.