O Proxy é um padrão estrutural que permite fornecer um substituto ou placeholder para outro objeto. Um proxy controla o acesso ao objeto original, permitindo que você faça algo antes ou depois do pedido chegar ao objeto original.
- Lazy loading: quando você tem um objeto pesado que consome recursos e nem sempre é necessário
- Controle de acesso: quando você quer que apenas clientes específicos possam acessar o objeto
- Execução local de serviço remoto: quando o objeto real está em outro servidor
- Logging: quando você precisa manter um registro de acessos ao objeto
- Caching: quando você precisa armazenar em cache resultados de operações custosas
from abc import ABC, abstractmethod
from typing import Dict
import time
# Interface comum
class BancoDados(ABC):
@abstractmethod
def consultar(self, query: str) -> Dict:
pass
@abstractmethod
def executar(self, comando: str) -> bool:
pass
# Objeto Real
class BancoDadosReal(BancoDados):
def __init__(self):
# Simulando uma conexão pesada
print("Inicializando conexão com o banco de dados...")
time.sleep(1)
self.conectado = True
def consultar(self, query: str) -> Dict:
if not self.conectado:
raise Exception("Banco de dados não está conectado!")
print(f"Executando query: {query}")
# Simulando uma consulta
return {"dados": f"Resultados para {query}"}
def executar(self, comando: str) -> bool:
if not self.conectado:
raise Exception("Banco de dados não está conectado!")
print(f"Executando comando: {comando}")
# Simulando execução de comando
return True
# Proxy
class BancoDadosProxy(BancoDados):
def __init__(self):
self._banco_dados = None
self._cache: Dict[str, Dict] = {}
def _conectar(self):
"""Lazy initialization do banco de dados real"""
if self._banco_dados is None:
self._banco_dados = BancoDadosReal()
def consultar(self, query: str) -> Dict:
print(f"Proxy: Verificando cache para query: {query}")
# Verifica cache primeiro
if query in self._cache:
print("Proxy: Retornando resultado do cache")
return self._cache[query]
# Se não está em cache, conecta ao banco se necessário
self._conectar()
# Executa a query e armazena em cache
resultado = self._banco_dados.consultar(query)
self._cache[query] = resultado
return resultado
def executar(self, comando: str) -> bool:
print(f"Proxy: Verificando permissão para executar: {comando}")
# Aqui poderia ter verificação de permissões
if "DROP" in comando.upper():
raise Exception("Proxy: Comando DROP não permitido!")
self._conectar()
return self._banco_dados.executar(comando)
# Cliente
class Aplicacao:
def __init__(self, banco: BancoDados):
self.banco = banco
def fazer_consulta(self, query: str):
try:
resultado = self.banco.consultar(query)
print(f"Aplicação recebeu: {resultado}\n")
except Exception as e:
print(f"Erro: {e}\n")
def executar_comando(self, comando: str):
try:
sucesso = self.banco.executar(comando)
print(f"Comando executado com sucesso: {sucesso}\n")
except Exception as e:
print(f"Erro: {e}\n")
# Exemplo de uso
def main():
# Criando o proxy
banco_proxy = BancoDadosProxy()
# Criando a aplicação
app = Aplicacao(banco_proxy)
# Fazendo consultas
app.fazer_consulta("SELECT * FROM usuarios")
# Segunda vez usa cache
app.fazer_consulta("SELECT * FROM usuarios")
# Executando comandos
app.executar_comando("INSERT INTO usuarios VALUES ('João')")
# Comando não permitido
app.executar_comando("DROP TABLE usuarios")
if __name__ == "__main__":
main()- Controle sobre o objeto real sem os clientes saberem
- Gerenciamento do ciclo de vida do objeto real
- Funciona mesmo quando o objeto real não está pronto ou disponível
- Adiciona comportamentos sem alterar o objeto real
- Princípio Aberto/Fechado: pode introduzir novos proxies sem mudar código existente
- Pode aumentar a complexidade do código
- Pode introduzir atraso na resposta quando inicializa sob demanda
- Alguns padrões de proxy podem tornar o comportamento menos previsível
- Pode dificultar o debugging em alguns casos
- Escolha o tipo de proxy adequado para seu caso (virtual, proteção, cache, etc.)
- Mantenha a interface do proxy idêntica à do objeto real
- Considere usar Factory Method para criar proxies
- Em Python, você pode usar
__getattr__para encaminhar chamadas automaticamente - Considere o impacto na performance ao adicionar camadas de proxy
- Documente claramente o comportamento adicional que o proxy introduz