O padrão de design Proxy tem como objetivo principal atuar como um substituto ou intermediário para outro objeto, controlando o acesso a ele.

Esse padrão é usado para resolver uma variedade de problemas relacionados à gestão de recursos e controle de acesso, e é particularmente útil em situações como:

  1. Controle de Acesso: O Proxy pode controlar o acesso ao objeto original, permitindo realizar operações adicionais, como autenticação, verificação de permissões, ou outras lógicas de segurança antes de permitir o acesso ao objeto.

  2. Atraso na Criação de Objetos Pesados (Lazy Initialization): Em casos onde a criação de um objeto é custosa em termos de recursos, o Proxy pode atrasar a criação e inicialização desse objeto até que ele seja realmente necessário.

  3. Gerenciamento de Recursos para Objetos Remotos (Remote Proxy): Em sistemas distribuídos, o Proxy pode facilitar a interação com objetos que residem em diferentes endereços de memória ou até mesmo em diferentes máquinas.

  4. Registro e Auditoria: O Proxy pode ser usado para registrar ou auditar as chamadas feitas ao objeto original, fornecendo uma forma de acompanhar as interações com o objeto sem modificar seu código.

  5. Referência Inteligente: O Proxy pode adicionar funcionalidades quando um objeto é acessado, como contar o número de referências ao objeto, carregar um objeto na memória quando ele é acessado pela primeira vez, ou liberar recursos quando o objeto não é mais necessário.

  6. Proteção e Restrição de Funcionalidades: O Proxy pode permitir a execução de certas operações enquanto restringe outras, protegendo o objeto original de usos inadequados ou perigosos.

Existem diferentes tipos de Proxies, como o Proxy Virtual, Proxy Remoto, Proxy de Proteção e Proxy Inteligente, cada um adaptado para resolver problemas específicos em diferentes contextos. O padrão Proxy é uma ferramenta poderosa para gerenciar o acesso e o ciclo de vida de objetos, proporcionando uma camada adicional de controle e abstração entre o cliente e o objeto real.

Exemplo de implementação

Vamos criar um exemplo de um Proxy que realiza a auditoria das chamadas feitas a um objeto original. Neste caso, o Proxy irá registrar cada chamada de método ao serviço real, permitindo o monitoramento e a análise dessas chamadas. Vou usar uma interface simples de um serviço que executa uma operação, e o Proxy irá registrar detalhes sobre quando e como as operações são chamadas.

Definindo a Interface do Serviço

public interface ServicoOperacao {
    void executarOperacao(String operacao);
}

Implementando o Serviço Real

public class ServicoOperacaoReal implements ServicoOperacao {
    @Override
    public void executarOperacao(String operacao) {
        System.out.println("Executando operação: " + operacao);
        // Implementação real da operação
    }
}

Implementando o Proxy de Auditoria

import java.util.Date;
 
public class ProxyAuditoria implements ServicoOperacao {
    private ServicoOperacao servicoReal;
 
    public ProxyAuditoria(ServicoOperacao servicoReal) {
        this.servicoReal = servicoReal;
    }
 
    @Override
    public void executarOperacao(String operacao) {
        // Registro de auditoria antes de chamar o método real
        System.out.println("Auditoria - Chamando operação: " + operacao + " em " + new Date());
        
        servicoReal.executarOperacao(operacao);
 
        // Registro de auditoria após a chamada do método
        System.out.println("Auditoria - Operação executada: " + operacao + " em " + new Date());
    }
}

Utilizando o Proxy de Auditoria no Sistema

public class Main {
    public static void main(String[] args) {
        ServicoOperacao servicoOperacaoReal = new ServicoOperacaoReal();
        ServicoOperacao servicoAuditoria = new ProxyAuditoria(servicoOperacaoReal);
 
        servicoAuditoria.executarOperacao("Operacao1");
        servicoAuditoria.executarOperacao("Operacao2");
    }
}