Wednesday, July 18, 2012

Design Patterns - Lazy Load

Neste post irei discutir um dos padrões EAA mais comuns hoje em dia em aplicações orientadas a dados, o padrão "Lazy Load".

Carregar dados do seu banco de dados para a memória e montar um objeto parece ser uma coisa muito simples. Quando trazemos os dados para memória, supostamente teremos um ganho na performance da aplicação.

Parece simples, mas não é. Hoje em dia a maioria das aplicações trabalham com objetos complexos, classes que contém tipos complexos dentro de si, ou seja objetos dentro de objetos. Isso gera uma cadeia de dados enorme dependendo do design do seu modelo.

Geralmente quando carregamos um objeto a partir do banco de dados,  todos os objetos relacionados a ele também são carregados em memória. Do ponto de vista do DBA esta é uma boa prática pois evita múltiplos acessos a base de dados. Do ponto de vista do programador esta prática facilita o desenvolvimento, pois quando ele for usar o objeto não vai precisar carregar todos os objetos explicitamente.


Quando pensamos no ponto de vista do administrador do servidor de aplicação, esta prática pode trazer mais problemas do que benefícios. Ao adotá-la estaremos alocando em memória objetos que, na maioria das vezes, não serão utilizados. Quando pensamos em aplicações multi-thread esta memória desperdiçada pode se tornar um problema enorme.


O padrão Lazy Load consiste em interromper este processo de carregamento dos objetos por um momento, deixando um marcador na estrutura do objeto que indicará que seus dados devem ser carregados. Esta carga só ocorrerá quando o objeto for utilizado de fato.


Existem 4 variações principais do padrão Lazy Load.
1- Lazy Initialization : Usa um marcador especial para indicar que o campo não foi carregado. Cada acesso ao campo vai checar o marcador e carregar o campo caso necessário.
2- Virtual Proxy:  Um objeto que implementa a mesma interface do objeto real. A primeira vez que um de seus métodos é chamado ele carrega o objeto real e delega.
3- Value Holder: Consiste um criar um método GetValue() no objeto. O cliente irá chamar o método e ele se encarrega de carregar o objeto na primeira chamada.
4- Ghost: É o objeto real, sem os dados preenchidos. A primeira vez que você chamar algum método, o objeto é carregado.


É comum vermos este padrão em algumas ferramentas de persistência como o LINQ to SQL e o Entity framework. Inclusive é possível definir quais propriedades devem aplicar este padrão em cada um dos seus objetos. No exemplo abaixo setamos a partir da propriedade Delay Loaded em um diagrama ORM do LINQ to SQL.






Neste exemplo de código abaixo temos o Lazy Loading aplicado no Entity Framework.  Nele, temos 10 contatos dos quais o usuário do programa irá selecionar um. Iremos carregar os pedidos do contato selecionado. Se o Lazy Loading não estivesse habilitado, nenhum dos pedidos seriam carregados pro contato.






Fontes:


http://msdn.microsoft.com
http://martinfowler.com

2 comments:

  1. Em objetos que contenha muitos tipos complexos e até mesmo em caso de objetos de relacionamento é interesante usar o Lazy false e quando necessario usar o metodo Include apenas no que realmente é necessario utilizar

    EX.
    var contact = context.contacts
    .Include("SalesOrderHeaders")
    .Include("OutraPropriedadeDeNavegacao")
    .where(c = > c.ContactID == contactID).FirstOrDefault();

    ReplyDelete
    Replies
    1. Muito bom Américo, complementou o post com um exemplo de carregamento explicito no Entity framework.

      Algumas vezes é necessário desabilitar o lazy load do contexto, isso reduz a quantidade de acessos feitos a base de dados.
      O problema desta abordagem é que o progamador não tem como saber em tempo de compilação quais objetos devem ser carregados explicitamente. Caso o lazy load esteja desabilitado na sua unidade de trabalho e o progamador tente acessar a propiedade sem ter feito o Include() no carregamento do objeto, ele vai receber um NullReference Exception em tempo de execução.

      Delete