Conheça o Pattern Proxy - GoF (Gang of Four)
Veja neste artigo como funciona o pattern Proxy, um pattern estrutural definido pelo GoF (Gang of Four) e cujo objetivo principal é encapsular um objeto através de um outro objeto que possui a mesma interface.
O Pattern Proxy é um padrão Estrutural definido pelo GoF (Gang of Four). O seu objetivo principal é encapsular um objeto através de um outro objeto que possui a mesma interface, de forma que o segundo objeto, conhecido como “Proxy”, controla o acesso ao primeiro, que é o objeto real.
Vantagens do Pattern Proxy
As principais vantagens de se utilizar o pattern Proxy são:
- Permite deixar transparente o local (endereço) do objeto real. O cliente não precisa conhecer se o objeto é remoto ou não, este tipo de proxy é conhecido como Remote Proxy.
- O pattern Proxy é muito utilizado pela tecnologia J2EE, pelo objeto “javax.ejb.EJBObject”, que representa uma referência remota ao EJB. Para o cliente que está utilizando a interface remote de um EJB, é transparente a chamada remota ao servidor, permitindo que complexos sistemas distribuídos possam ser desenvolvidos como se fossem chamadas locais.
- O consagrado framework Hibernate também utiliza o pattern Proxy, por exemplo, ao fazer o “lazy-loading”, técnica utilizado para acessar o banco de dados apenas quando for necessário. Muitas vezes quando trabalhamos com o Hibernate, e uma busca é realizada, por exemplo usando o método “session.load(id)”, um Proxy para o objeto real é retornado. Neste caso o objeto ainda não está completamente preenchido, pois nenhum SQL foi realizado até este momento. Apenas quando uma propriedade deste objeto (métodos getX) ou um relacionamento, como por exemplo “empresa.getFuncionarios()” forem chamados, a consulta no banco será realizada. Tudo isto de forma transparente para o cliente.
- Útil para realizar otimizações, como cache de objetos. Também pode ser implementado rotinas de logs e controle de acesso (segurança). Este tipo de proxy é conhecido como Virtual Proxy.
Uma vez que neste padrão o objeto real é encapsulado, o objeto Proxy pode armazenar o resultado de um acesso em cache. Por exemplo, se um Remote Proxy é utilizado, o custo deste acesso remoto pode ser grande para a aplicação, neste caso o Proxy salva localmente os resultados em cache, diminuindo assim, a quantidade de acessos remotos.
Diagrama
Abaixo o diagrama do pattern Proxy:
O diagrama demonstra que o objeto Proxy e o objeto real (RealSubject) implementam a mesma interface, neste caso chamada de Subject. O Proxy encapsula o acesso ao RealSubject.
Exemplo de Código
Abaixo temos um exemplo de código. Crie uma classe “ProxyExample” e execute o código:
import java.util.ArrayList;
import java.util.List;
/**
* Imagine que esta classe faz acessos ao banco de dados
*/
class PessoaDAO {
public static Pessoa getPessoaByID(String id){
System.out.println("select * from PESSOA where id="+id);
return new PessoaImpl(id,"Pessoa " + id);
}
}
/**
* Interface comum entre o objeto real e o Proxy
*/
interface Pessoa {
public String getNome();
public String getId();
}
/**
* Objeto real
*/
class PessoaImpl implements Pessoa {
private String nome;
private String id;
public PessoaImpl(String id,String nome) {
this.id = id;
this.nome = nome;
// apenas para simular algo...
System.out.println("Retornou a pessoa do banco de dados -> " + nome);
}
public String getNome() {
return nome;
}
public String getId() {
return this.id;
}
}
class ProxyPessoa implements Pessoa {
private String id;
private Pessoa pessoa;//mesma interface
public ProxyPessoa(String nome) {
this.id = nome;
}
/**
* Método comum da interface
*
* @see Pessoa#getNome()
*/
public String getNome() {
if (pessoa == null) {
//Apenas cria o objeto real quando chamar este método
pessoa = PessoaDAO.getPessoaByID(this.id);
}
/** Delega para o objeto real **/
return pessoa.getNome();
}
public String getId() {
return this.id;
}
}
/**
* Exemplo adaptado de "http://en.wikipedia.org/wiki/Proxy_pattern"
*/
public class ProxyExample {
public static void main(String[] args) {
List<Pessoa> pessoas = new ArrayList<Pessoa>();
//Cria cada Proxy para encapsular o acesso a classe "PessoaImpl"
pessoas.add(new ProxyPessoa("A"));
pessoas.add(new ProxyPessoa("B"));
pessoas.add(new ProxyPessoa("C"));
System.out.println("Nome: " + pessoas.get(0).getNome());
// busca do banco de dados
System.out.println("Nome: " + pessoas.get(1).getNome());
// busca do banco de dados
System.out.println("Nome: " + pessoas.get(0).getNome());
// já buscou esta pessoa... apenas retorna do cache...
// A terceira pessoa nunca será consultada no banco de dados
(melhor performance - lazy loading)
System.out.println("Id da 3ª - " + pessoas.get(2).getId());
//pode imprimir o ID do objeto, e o proxy nao será inicializado.
}
}
A saída do programa é a seguinte:
select * from PESSOA where id=A
Retornou a pessoa do banco de dados -> Pessoa A
Nome: Pessoa A
select * from PESSOA where id=B
Retornou a pessoa do banco de dados -> Pessoa B
Nome: Pessoa B
Nome: Pessoa A
Id da 3ª – C
Conforme demonstrado no exemplo, uma consulta ao banco de dados é realizada apenas quando o método getNome da Pessoa é chamado. O Proxy controla este acesso, controlando a consulta para esta ser realizada apenas uma vez.
Note que o método pessoas.get(2).getId() foi chamado para imprimir o ID da “Pessoa C”, e isto não inicializou o proxy.
Lembre-se: o importante é que o Proxy e o objeto real que está sendo encapsulado devem implementar a mesma interface.
Conclusão
O Pattern Proxy é muito utilizado em aplicações J2EE e por alguns frameworks, espero que tenham gostado deste breve artigo, e até a próxima.
Artigos relacionados
-
Artigo
-
Artigo
-
Artigo
-
Artigo
-
Artigo