Lazy load?

Hail!

Imaginem um cenário onde exite uma relação de 1—*. Como, por exemplo, um Cliente possui N pedidos. Geralmente representamos essa relação dessa forma

[code]public class Cliente {

private Collection<Pedido> pedidos;

}
[/code]

E meu ClienteMapper quando busca do banco recupera as informações do cliente e popula essa collection de Pedido com outra tabela no banco (geralmente utilizando um PedidoMapper).

Agora, imaginem que em um dado momento eu só queira alterar o nome de um usuário. Se meu ClienteMapper já popula a collection de Pedidos eu não teria um overhead?

A solução seria implementar Lazy Load, certo? E, supondo que não esteja utilizando nenhum “framework faz-tudo”, como implementaria isso? Uma maneira que pensei foi adicionar o código

private RepositorioPedido repositorioPedido;

public Collection<Pedido> getPedidos(){
	if(pedidos == null)
		pedidos = repositorioPedido.buscaPorCliente(getId());
	return pedidos;
}

Na minha entidade Cliente. (suponhamos que repositorioPedido seja uma abstração para um Mapper, ou seja, um PedidoMapper implementa esse repositorio).

Será que essa seria a melhor abordagem para implementação de lazy load? Será que não fica complexo ter que gerenciar a dependência criada (um cliente precisaria sempre de uma instancia de repositorioPedido).

Como expor para um cliente (alguem da camada da aplicação) que necessita criar uma instancia da classe Cliente? Disponibilizaria um Builder pra ele? Um Factory?

Não sei se consegui ser claro, mas gostaria de ouvir(ler) a opiniao de vocês!

Valeu!

Eu gostaria que não fosse necessário ter esse tipo de situação, mas acredito que essa é a forma mais simples e clara de se fazer. Você pode usar o Spring pra garantir que o repositório vai ser sempre injetado e testes unitários pra garantir que tudo vai funcionar. Nesse caso, acredito que não vale a pena ficar inventando pra fazer alguma coisa mais complicada. Se for o caso, melhor utilizar framework.

Como o colega disse, use IOC/DI pra isso.

Tanto Factory quanto builder são válidos. O que você precisa definir é se irá cria-los para gerar o objeto já em estado persistente, ou não.

Concordo com a utilização do framework. Só que no meu caso isso é impossível… :wink:

O que você quer dizer em gerar um objeto em estado persistente?? Concordo que na criação desse objeto não será necessário o repositorioPedido, pois não tem como esse objeto ter pedidos já que acabou de ser criado, nem adicionar pedidos a um cliente recém criado.

Mas isso me faz pensar sobre a consistência do objeto. Se um objeto tem um atributo que ele utiliza, na minha opinião, ele é inconsistente caso esse atributo não exista (ou seja = null). O que você acha?

Concordo contigo, Danilo, no que diz respeito a manter a consistencia do objeto. Acho importante sempre ter o objeto válido com todas as dependências injetadas.

Nesse caso acredito que a melhor solução e mais simples seria injeção das dependências via construtor e criação do objeto utilizando factory pra injetar todas as dependências.

O que você quer dizer em gerar um objeto em estado persistente?? Concordo que na criação desse objeto não será necessário o repositorioPedido, pois não tem como esse objeto ter pedidos já que acabou de ser criado, nem adicionar pedidos a um cliente recém criado.

Mas isso me faz pensar sobre a consistência do objeto. Se um objeto tem um atributo que ele utiliza, na minha opinião, ele é inconsistente caso esse atributo não exista (ou seja = null). O que você acha?[/quote]
Uma factory para estado persistente vai criar o objeto e persisti-lo no banco. Isso pode ser bom ou não, dependerá do que você quer. Deve ser usado com moderação nos casos onde for necessário. O Active Record do Rails tem essa funcionalidade e é muito útil. Quanto ao estado válido, basta você definir as invariantes do seu objeto e garantir que elas não serão violadas.

Opa, Emerson!

Desculpa minha ignorância, mas não entendi direito o que você quis dizer com [quote=Emerson Macedo]definir as invariantes do seu objeto e garantir que elas não serão violadas[/quote]

Nesse caso um repositorioPedido com valor null não seria uma inconsistência do meu objeto, uma vez que ele não é capaz de se comportar conforme o esperado? (É sempre culpa do banco de dados… Persiste o objeto. Recupera o objeto. bla bla bla)

Também não entendi o lance da factory para estado persistente. Você disse que a fábrica seria responsável por criar o objeto E persisti-lo no banco? E qual a relação disso com o Active Record (onde o objeto que é responsável pela sua persistência)?

Vlw :wink:

[quote=Danilo Barboza]Opa, Emerson!

Desculpa minha ignorância, mas não entendi direito o que você quis dizer com [quote=Emerson Macedo]definir as invariantes do seu objeto e garantir que elas não serão violadas[/quote]

Nesse caso um repositorioPedido com valor null não seria uma inconsistência do meu objeto, uma vez que ele não é capaz de se comportar conforme o esperado? (É sempre culpa do banco de dados… Persiste o objeto. Recupera o objeto. bla bla bla)[/quote]
Você precisa saber se seu objeto está em estado válido/inválido. Isso você faz verificando se os seus atributos estão com valores que você considera válidos (o espaço-estado do seu objeto). Se null não faz parte do espaço-estado do seu objeto (eu acredito que não fará), logo seu objeto está em estado inválido. Teve uma discussão sobre isso em outra thread. http://www.guj.com.br/posts/list/103549.java

Pode ser que o nome Active Record te confundiu. Vou melhorar a explicação: Uma coisa é o Pattern Active Record [Fowler, PEAA], outra é o ActiveRecord do framework Rails (Que implementa o Pattern mencionado). Esse último, tem um método create, que em Java poderia ser um método estático na sua classe. esse método cria o objeto e já grava no banco. Você nem é obrigado a estar usando o Pattern Active Record pra fazer isso. Você poderia apenas criar esse método no seu objeto Java. Ex:

public class SuaClasse {
    //Atributos e outros
    public static SuaClasse criar(Tipo1 parametro1, Tipo2 parametro2) {
        //Cria o objeto
        //Faz as validações de estado do objeto
        //Salva no banco (pode usar o Repository, se for o caso)
        //Retorna o objeto em estado consistente e válido
    }
}

Isso foi algo simples que pensei rapidamente. Na verdade você pode fazer da forma que quiser.
A implementação do rails você pode ver em em: http://ar.rubyonrails.com/classes/ActiveRecord/Base.html#M000339

[]s

[quote=Danilo Barboza]Hail!

Imaginem um cenário onde exite uma relação de 1—*. Como, por exemplo, um Cliente possui N pedidos. Geralmente representamos essa relação dessa forma

[code]public class Cliente {

private Collection<Pedido> pedidos;

}
[/code]

[/quote]

O problema já começa ai. Uma relação 1-N é definida assim:

[code]public class Cliente {

public Collection<Pedido> getPedidos(){
           // procura aqui. Em particular pode retorna um atributo, mas isso não é obrigatorio.
    }

}
[/code]

Sem um framework faz tudo ( aka sem Injetores de Dependencia) vc faria assim :

[code]public class Cliente {

public Collection<Pedido> getPedidos(){
           return repositorioPedido.buscaPorCliente(this.getId());
    }

}
[/code]

Que é quase o que vc escreveu :wink:

Agora, aquele codigo faz lazy-loading ,mas não faz cache. Ou seja, cada vez que for chamado o metodo executa a pesquisa.
Para lazy loading com cache vc pode usar uma técnica diferente semelhante ou que criou

[code]public class Cliente {

    Collection<Pedidos> pedidos = new LazyCollection<Pedidos>(){
       
             protected Collection<Pedidos> retrive(){
                     return repositorioPedido.buscaPorCliente(this.getId());
              }
   }

public Collection<Pedido> getPedidos(){
         return  pedidos;
    }

}
[/code]

A classe LazyCollection é uma classe no interface de collection mas que atua como Proxy para a coleção real
Todas as operações chamam o método Load() que verifica se a coleção real está carrega e se não chama o método para a encontrar.

[code]public class LazyCollection implements Collection{

 private Collectio<T> realCollection; 

 private load(){
       if (realCollection==null){
             realCollection = retrive();
      }
}

protected Collection<T> retrive(){
     return Collections.emptySet(); // must override to do something usefull
}

// alguns exemplos
public int size (){
load();
return realCollection.size();
}

 public Iterator<T> iterator (){
       load();
       return realCollection.iterator ();
 }

}[/code]

Existe uma outra opção que é usar a classe Proxy do java. Esta classe permite que vc crie um Proxy dinamicamente.
Vc só tem que declarar a interface que o proxy tem que ter ( Collection) e passar um handler que será invocado para cada método. Nesse handler vc pode incluir a logica de load e retrive. O detalhe é que vc precisa dizer como obter os objetos e isso
acaba complicando as coisas . a menos que vc crie um método genérico que funciona para qualquer entidade e repositorio.