Proxy
Fornece um substituto ou placeholder para outro objeto para controlar o acesso a ele.
Intenção
Introduzir um objeto substituto que controla o acesso a um sujeito real -- adiando sua criação, protegendo suas operações, ou adicionando preocupações transversais -- enquanto apresenta a mesma interface, de forma que os clientes não consigam notar a diferença.
Problema
Você tem um objeto que é caro de criar, está localizado em um servidor remoto, ou exige controle de acesso. Permitir acesso direto irrestrito desperdiça recursos, compromete segurança, ou acopla o cliente a detalhes de infraestrutura. Você precisa de uma forma de interpor lógica de controle sem alterar o cliente ou o objeto real.
Solução
Crie uma classe proxy que implementa a mesma interface do sujeito real. O proxy mantém uma referência (ou a cria preguiçosamente) ao sujeito real e encaminha requisições a ele, adicionando comportamento como inicialização preguiçosa, verificações de acesso, logging, ou cache ao redor de cada delegação.
Participantes
- Subject -- a interface comum compartilhada por RealSubject e Proxy
- RealSubject -- o objeto real que executa o trabalho
- Proxy -- controla o acesso ao RealSubject; implementa a mesma interface
- Client -- trabalha com a interface Subject, sem saber se está lidando com um Proxy ou com o RealSubject
Vantagens
- Controla o acesso ao objeto real sem que os clientes saibam
- Permite inicialização preguiçosa de objetos pesados (virtual proxy)
- Pode adicionar cache, logging, ou controle de acesso de forma transparente
- O ciclo de vida do objeto real pode ser gerenciado independentemente do cliente
Desvantagens
- Adiciona uma camada extra de indireção, o que pode complicar a depuração
- O tempo de resposta pode aumentar devido a processamento adicional no proxy
- O código do proxy pode ficar complexo ao combinar múltiplas preocupações (cache + auth + logging)
- O proxy precisa se manter sincronizado com a evolução da interface do sujeito real
Analogia do mundo real
Um cartão de crédito é um proxy para sua conta bancária. A loja (cliente) passa o cartão (proxy) em vez de acessar seu dinheiro (sujeito real) diretamente. O cartão faz o controle de acesso (verificação de PIN), se comunica com o banco, e pode aplicar limites de gasto -- tudo antes de deixar a transação passar.
Casos de uso
- Virtual proxy: adiar o carregamento de uma imagem de alta resolução até que ela entre na área visível
- Protection proxy: checar permissões do usuário antes de permitir operações em um recurso sensível
- Caching proxy: armazenar resultados de consultas caras ao banco de dados e retornar dados em cache em chamadas repetidas
- Remote proxy: representar um objeto em um servidor diferente e encapsular chamadas pela rede
- Logging proxy: registrar cada invocação de método para fins de auditoria ou depuração
Exemplos de código
interface Image {
void display();
String getFilename();
}
final class HighResImage implements Image {
private final String filename;
private final String data;
HighResImage(String filename) {
this.filename = filename;
// Simulate expensive loading
this.data = "[binary data of " + filename + "]";
System.out.println("Loaded " + filename + " from disk (expensive!)");
}
@Override
public void display() {
System.out.println("Displaying " + filename);
}
@Override
public String getFilename() {
return filename;
}
}
// Virtual Proxy -- defers creation until actually needed
final class LazyImageProxy implements Image {
private final String filename;
private HighResImage realImage;
LazyImageProxy(String filename) {
this.filename = filename;
}
@Override
public void display() {
if (realImage == null) {
realImage = new HighResImage(filename);
}
realImage.display();
}
@Override
public String getFilename() {
return filename;
}
}
// Protection Proxy -- checks permissions before delegating
final class ProtectedImageProxy implements Image {
private final Image target;
private final boolean isAdmin;
ProtectedImageProxy(Image target, boolean isAdmin) {
this.target = target;
this.isAdmin = isAdmin;
}
@Override
public void display() {
if (!isAdmin) {
System.out.println("Access denied to " + target.getFilename());
return;
}
target.display();
}
@Override
public String getFilename() {
return target.getFilename();
}
}
public class ProxyDemo {
public static void main(String[] args) {
Image image = new LazyImageProxy("sunset.png");
System.out.println("Proxy created, image not loaded yet");
image.display(); // loads here, on first access
image.display(); // reuses cached instance
}
}Um virtual proxy que carrega preguiçosamente um objeto de imagem pesado no primeiro acesso, e um protection proxy que verifica permissões antes de delegar operações.
Padrões relacionados
Decorator
Anexa responsabilidades adicionais a um objeto dinamicamente, oferecendo uma alternativa flexível à subclassificação para estender funcionalidade.