Proxy
Intenção (GoF)
Seção intitulada “Intenção (GoF)”Fornecer um representante ou substituto de outro objeto para controlar o acesso a ele.
Problema que resolve
Seção intitulada “Problema que resolve”Às vezes acessar um objeto diretamente é indesejável: ele é caro para criar (imagem de alta resolução, conexão), vive em outro processo/máquina, exige verificação de permissão, ou seus resultados poderiam ser cacheados. Não queremos poluir o objeto real com essas preocupações, nem mudar o código cliente. O Proxy resolve interpondo-se: implementa a mesma interface e adiciona a lógica de controle antes/depois de delegar ao objeto real.
O problema, em código
Seção intitulada “O problema, em código”public interface RepositorioDocumento { String conteudo(String id);}
public class RepositorioDocumentoReal implements RepositorioDocumento { public String conteudo(String id) { return consultaBancoLenta(id); } private String consultaBancoLenta(String id) { System.out.println("Consulta cara ao banco para " + id); return "Conteúdo do documento " + id; }}Cada chamada de conteudo(id) bate no banco, mesmo para o mesmo id repetido. Adicionar cache dentro do repositório real misturaria a responsabilidade de persistência com a de cache.
Solução com o padrão (proxy de cache + lazy)
Seção intitulada “Solução com o padrão (proxy de cache + lazy)”import java.util.HashMap;import java.util.Map;
public class RepositorioDocumentoProxy implements RepositorioDocumento {
private RepositorioDocumentoReal real; private final Map<String, String> cache = new HashMap<>();
public String conteudo(String id) { String emCache = cache.get(id); if (emCache != null) { System.out.println("Cache hit para " + id); return emCache; } if (real == null) { real = new RepositorioDocumentoReal(); } String resultado = real.conteudo(id); cache.put(id, resultado); return resultado; }}Uso — o cliente continua programando contra a interface, sem saber que fala com um proxy:
RepositorioDocumento repo = new RepositorioDocumentoProxy();System.out.println(repo.conteudo("doc-42"));System.out.println(repo.conteudo("doc-42"));O proxy adia a criação do objeto real (lazy) até a primeira necessidade e serve chamadas repetidas do cache, sem qualquer alteração no RepositorioDocumentoReal nem no cliente.
Estrutura
Seção intitulada “Estrutura”classDiagram
class RepositorioDocumento {
<<interface>>
+conteudo(String) String
}
class RepositorioDocumentoReal {
+conteudo(String) String
}
class RepositorioDocumentoProxy {
-real : RepositorioDocumentoReal
-cache : Map
+conteudo(String) String
}
class Cliente
RepositorioDocumento <|.. RepositorioDocumentoReal
RepositorioDocumento <|.. RepositorioDocumentoProxy
Cliente --> RepositorioDocumento : usa
RepositorioDocumentoProxy --> RepositorioDocumentoReal : controla acesso
Tipos de Proxy
Seção intitulada “Tipos de Proxy”| Tipo | Propósito | Exemplo |
|---|---|---|
| Virtual | Adiar criação/carga de um objeto caro até ser realmente necessário (lazy) | Carregar imagem só quando exibida; ORM lazy-loading de associações |
| Remoto | Representar localmente um objeto em outro processo/máquina, escondendo a rede | Stub de RMI/gRPC; cliente de serviço remoto |
| Proteção | Controlar acesso conforme permissões | Verificar papel do usuário antes de delegar |
| Cache / Smart reference | Memorizar resultados ou contar referências, log, lazy-init | O exemplo acima; contagem de referências |
Proxy vs. Decorator
Seção intitulada “Proxy vs. Decorator”Estruturalmente idênticos — ambos implementam a mesma interface do objeto envelopado e delegam a ele. A diferença é de intenção:
| Proxy | Decorator | |
|---|---|---|
| Intenção | Controlar o acesso ao objeto real | Adicionar responsabilidades/comportamento |
| Quem controla o ciclo de vida | Geralmente o próprio proxy (cria o real, faz lazy) | O cliente monta a cadeia; o objeto já existe |
| Composição | Normalmente um proxy por objeto | Encadeamento de vários decorators |
| Interface | Igual à do objeto real | Igual à do objeto real |
Regra prática: se você está gerenciando quando/se/como o objeto real é acessado, é Proxy. Se está enriquecendo o resultado ou o comportamento de uma chamada, é Decorator.
Consequências e trade-offs
Seção intitulada “Consequências e trade-offs”- A favor: introduz controle de acesso, lazy, cache e representação remota de forma transparente, respeitando SRP (o objeto real não conhece cache/segurança/rede).
- Contra: uma camada de indireção a mais; proxies de lazy podem esconder custos (uma chamada “barata” dispara I/O); complexidade de invalidação de cache.
Anti-padrões e erros comuns
Seção intitulada “Anti-padrões e erros comuns”- Confundir com Decorator: mesma forma, intenção diferente (ver tabela acima).
- Cache sem estratégia de invalidação: proxy de cache que nunca expira serve dados obsoletos.
- Proxy que altera semântica: um proxy deve preservar o contrato da interface (LSP); mudar comportamento observável quebra o cliente.
- Lazy escondendo latência crítica: proxy virtual que dispara chamadas remotas caras em getters aparentemente triviais surpreende quem consome.
Relações
Seção intitulada “Relações”- Categoria: Padrões Estruturais.
- Índice: Design Patterns (GoF).
- Padrão estrutural de forma semelhante e intenção diferente: comparar com Decorator (ver Padrões Estruturais).
- Simplificação de subsistema (intenção distinta): Facade.
- Princípios: SRP - Single Responsibility Principle, LSP - Liskov Substitution Principle.
Perguntas de revisão
Seção intitulada “Perguntas de revisão”1. Qual é a intenção do Proxy e quais seus principais tipos?
Resposta
Controlar o acesso a outro objeto por meio de um substituto com a mesma interface. Tipos: virtual (lazy loading de objeto caro), remoto (representar objeto em outro processo/máquina), proteção (controle de acesso por permissão) e cache/smart reference (memorização, contagem, log).
2. Proxy e Decorator têm a mesma estrutura; como distingui-los?
Resposta
Pela intenção. Proxy controla o acesso (decide quando/se/como o objeto real é usado, muitas vezes gerenciando seu ciclo de vida). Decorator adiciona comportamento/responsabilidades ao resultado, tipicamente encadeando vários envelopes sobre um objeto que já existe.
3. Como o Proxy ajuda a respeitar o SRP?
Resposta
Ele extrai preocupações transversais (cache, segurança, lazy, rede) do objeto real, que permanece focado em sua responsabilidade central. O controle de acesso vive no proxy, não misturado à lógica de negócio/persistência.
4. Que armadilha um proxy virtual (lazy) pode introduzir?
Resposta
Esconder custos: uma chamada que parece barata pode disparar criação de objeto pesado ou I/O remoto na primeira invocação. Isso surpreende o cliente e pode causar latência inesperada em pontos críticos. Também há o risco de cache sem invalidação servindo dados obsoletos.