Proxy Pattern: Exemplo e Aplicação
- c#
- design patterns
Desde meu último post acabei não cumprindo com o que esperava. Como já devo ter mencionado, estou cursando Engenharia da Computação Cooperativo, onde o curso intercala módulo acadêmico e estágio, permitindo tempo integral (desde que não tenha DPs para pagar). Comento sobre isso em outro post, vamos voltar ao assunto.
Depois de realizar meu primeiro laboratório de engenharia de software, não adotar um framework na minha camada de persistência tornou muito trabalhoso sua modelagem e manutenção. Cada operação exigia escrita em código ou stored procedure o que tornava "uma tarefa simples" em trabalhosa, não que demorasse muito, mas depois de muitas linhas de coisas para coisas tão banais o processo ficava um tanto... chato.
Desde então, tenho como objetivo aprender um framework para essa finalidade. Claro, não podia ficar de lado o Hibernate, mas no momento estou curtindo C# e no caso temos o NHibernate. Conversei com um colega do trabalho e ele aparentemente não gostou muito do NHibernate. Não entrei muito em detalhes do por que ele era ruim, mas a comparação era bem direta se comparado com o Hibernate do Java. Portanto, ele costuma usar o ActiveRecord que usa como base o NHibernate.
Mas nas últimas semanas saiu o NHibernate 2.0, apesar de a página não estar atualizada. E concerteza deve ter melhorado em muitos aspectos. Por isso escolhi o NHibernate como estudo. Pretendo falar mais sobre ele em outro post (quando der).
Resumo
O uso do (N)Hibernate é bem direto, use POJO (Plain Old Java Object) ou POCO (Plain Old C# Object), onde os objetos contém basicamente get/set. Talvez minha definição esteja um tanto incompleta, então me corrijam se falei absurdos.
No Java todos os métodos por default podem ser overrided, bastando reescrevê-lo em sua classe estendida. Já no C# a coisa fica mais explicita. Por default nenhum método/property pode ser overrided, para tal é necessário acrescentar a keyword "virtual", e quando realizamos um override a keyword "override", já veremos um código.
O que quero dizer com isso? Bem, como no Java isso já é default para todos os objetos então só é necessário definir os getters e setters da classe, já para o NHibernate todos os properties devem vir com a keyword virtual, caso contrário a coisa não funciona.
Então me perguntei: por que tenho que colocar todas as properties como 'virtual'?
Bom, inicialmente achei que o NHibernate deveria simplesmente instanciar um objeto da entidade mapeada e então preencher todos os properties um a um. Mas o (N)Hibernate existe uma funcionalidade chamada de Lazy Load.
O que é Lazy Load?
Serei bem breve, digamos que você tenha um objeto e este possui uma referência para um outro objeto, seria um desperdício recurarmos o estado do objeto referenciado se nem usassémos ele. O que o lazy load faz é carregar este objeto (realizar a query no banco de dados) realmente quando precisamos. Mas como diabos isso ocorre?
A resposta é usando Proxy Pattern.
O que ele faz é atuar como um proxy entre a chamada de um property/método. Uau, entendi nada, o que é proxy?
Eu sempre me perguntava isso. Um proxy é digamos um "passo" intermediário de um fluxo. O exemplo mais comum é em redes de computadores. Digamos que sua máquina acesse a internet diretamente, ou seja, o seu computador e a "nuvem". Se quisessemos realizar um filtro no neste tráfego, poderíamos utilizar um serviço de proxy que atua entre a nuvem e seu computador, de modo que todas as requisições passem sempre pelo proxy, tratando os dados trafegados e aplicando filtros adequadamente.
Como implementamos um proxy pattern? Veja um exemplo bem simples abaixo:
public class MyClass
{
public virtual string Message { get; set; }
public virtual void DoSomething()
{
Console.WriteLine("Doing something really important.");
}
}
public class MyClassProxed : MyClass
{
public override string Message
{
get
{
Console.WriteLine("Proxying on reading...");
return base.Message;
}
set
{
Console.WriteLine("Proxying on saving...");
base.Message = value;
}
}
public override void DoSomething()
{
Console.WriteLine("Proxying DoSomething() before");
base.DoSomething();
Console.WriteLine("Proxying DoSomething() after");
}
}
O que basicamente fazemos é extender a classe MyClass
criando uma MyClassProxed
dando override nos métodos e properties. Para testarmos, seria muito simples:
MyClass myClass = new MyClass() {Message = "Hello World!"};
Console.WriteLine("Normal behavior");
Console.WriteLine("MyClass.Message = {0}", myClass.Message);
Console.Write("MyClass.DoSomething()");
myClass.DoSomething();
Console.WriteLine();
Console.WriteLine("Normal behavior plus proxying");
myClass = new MyClassProxed() {Message = "HelloWorld!"};
Console.WriteLine("MyClass.Message = {0}", myClass.Message);
Console.Write("MyClass.DoSomething()");
myClass.DoSomething();
Como a classe MyClassProxed
extende a class MyClass
basicamente a interface não muda, logo do nosso ponto de vista qual implementação usamos não importa, seja MyClass
ou MyClassProxed
, para nós o que importa é a interface da property Message e o método DoSomething()
. O resultado esperado é algo como:
Conclusão
Utilizando este pattern é possível realizar o lazy load de forma transparente para o desenvolvedor.
Esse pattern é bem conhecido e pode ser encontrado no livro Design Patterns, Elements of Reusable Object-Oriented Software do GoF (Gang Of Four), que por sinal é uma leitura básica para qualquer desenvolver.
Infelizmente, o conteúdo do livro não é de fácil digestão quando sua experiência como desenvolvedor é pequena, muitos patterns não fazem sentido pois você não consegue comporrender os problemas de implementação que existem.
Espero poder dar continuidade neste tipo de post, até uma próxima vez.