Padrões estruturais: Decorator e Proxy
1. Introdução aos Padrões Decorator e Proxy
Os padrões estruturais Decorator e Proxy compartilham um objetivo arquitetural comum: adicionar ou controlar comportamento sem alterar a interface do objeto original. Ambos utilizam composição e implementam a mesma interface do objeto alvo, permitindo que clientes interajam com objetos decorados ou proxy como se fossem o objeto real.
A diferença fundamental reside na intenção: o Decorator adiciona responsabilidades dinamicamente, estendendo funcionalidades de forma transparente; o Proxy controla o acesso ao objeto real, gerenciando seu ciclo de vida, segurança ou latência.
Em termos de princípios SOLID, ambos promovem:
- Princípio Aberto/Fechado (OCP): sistemas podem ser estendidos sem modificar código existente
- Princípio da Responsabilidade Única (SRP): cada decorator ou proxy gerencia uma preocupação específica
2. Padrão Decorator – Estrutura e Funcionamento
O Decorator é composto por quatro elementos principais:
- Componente abstrato: interface que define as operações comuns
- ConcreteComponent: implementação base do componente
- Decorator abstrato: mantém referência ao componente e delega chamadas
- ConcreteDecorators: estendem funcionalidades via composição recursiva
// Interface do componente
public interface IDataProcessor {
string Process(string data);
}
// Implementação base
public class BaseProcessor : IDataProcessor {
public string Process(string data) {
return data.ToUpper();
}
}
// Decorator abstrato
public abstract class DataProcessorDecorator : IDataProcessor {
protected IDataProcessor _processor;
public DataProcessorDecorator(IDataProcessor processor) {
_processor = processor;
}
public virtual string Process(string data) {
return _processor.Process(data);
}
}
// Decorators concretos
public class ValidationDecorator : DataProcessorDecorator {
public ValidationDecorator(IDataProcessor processor) : base(processor) {}
public override string Process(string data) {
if (string.IsNullOrEmpty(data))
throw new ArgumentException("Dados inválidos");
return base.Process(data);
}
}
public class LoggingDecorator : DataProcessorDecorator {
public LoggingDecorator(IDataProcessor processor) : base(processor) {}
public override string Process(string data) {
Console.WriteLine($"Processando: {data}");
var result = base.Process(data);
Console.WriteLine($"Resultado: {result}");
return result;
}
}
3. Aplicações Arquiteturais do Decorator
O Decorator é ideal para adicionar camadas de funcionalidade sem poluir o núcleo do sistema. Exemplos arquiteturais incluem:
- Pipeline de processamento de dados: validação → formatação → compressão → persistência
- Sistemas de cache: adicionar cache em memória para consultas frequentes
- Logging e auditoria: registrar chamadas sem modificar a lógica de negócio
- Criptografia: adicionar camadas de segurança em transmissão de dados
// Exemplo de pipeline com empilhamento
IDataProcessor pipeline = new BaseProcessor();
pipeline = new ValidationDecorator(pipeline);
pipeline = new LoggingDecorator(pipeline);
pipeline = new CompressionDecorator(pipeline);
var result = pipeline.Process("dados importantes");
A vantagem arquitetural é a flexibilidade em tempo de execução, evitando a explosão de subclasses que ocorreria com herança tradicional.
4. Padrão Proxy – Estrutura e Funcionamento
O Proxy compartilha uma interface comum (Subject) com o RealSubject, mas controla o acesso a ele. Existem três tipos principais:
- Proxy Virtual: lazy loading de recursos pesados
- Proxy de Proteção: controle de acesso e segurança
- Proxy Remoto: abstração de comunicação em rede
// Interface comum
public interface IImage {
void Display();
}
// Objeto real (pesado)
public class RealImage : IImage {
private string _filename;
public RealImage(string filename) {
_filename = filename;
LoadFromDisk();
}
private void LoadFromDisk() {
Console.WriteLine($"Carregando imagem {_filename}");
}
public void Display() {
Console.WriteLine($"Exibindo {_filename}");
}
}
// Proxy Virtual com lazy loading
public class ImageProxy : IImage {
private RealImage _realImage;
private string _filename;
public ImageProxy(string filename) {
_filename = filename;
}
public void Display() {
if (_realImage == null) {
_realImage = new RealImage(_filename);
}
_realImage.Display();
}
}
// Proxy de Proteção
public class SecureImageProxy : IImage {
private RealImage _realImage;
private string _filename;
private string _userRole;
public SecureImageProxy(string filename, string userRole) {
_filename = filename;
_userRole = userRole;
}
public void Display() {
if (_userRole != "Admin") {
throw new UnauthorizedAccessException("Acesso negado");
}
if (_realImage == null) {
_realImage = new RealImage(_filename);
}
_realImage.Display();
}
}
5. Aplicações Arquiteturais do Proxy
O Proxy resolve problemas arquiteturais específicos:
- Lazy loading: adiar carregamento de imagens, conexões de banco ou arquivos grandes
- Controle de acesso: autenticação/autorização em APIs e microsserviços
- Proxy remoto: comunicação transparente via gRPC, RMI ou WebSockets
- Cache distribuído: armazenar resultados de consultas frequentes
// Exemplo de proxy remoto em microsserviços
public class RemoteServiceProxy : IService {
private HttpClient _client;
private string _baseUrl;
public async Task<Data> GetDataAsync(int id) {
var response = await _client.GetAsync($"{_baseUrl}/api/data/{id}");
return await response.Content.ReadAsAsync<Data>();
}
}
6. Comparação entre Decorator e Proxy
| Aspecto | Decorator | Proxy |
|---|---|---|
| Intenção | Adicionar responsabilidades | Controlar acesso |
| Composição | Empilhamento recursivo | Referência única |
| Ciclo de vida | Gerencia funcionalidades | Gerencia o objeto real |
| Transparência | Total para o cliente | Pode adicionar lógica de segurança |
| Exemplo típico | Logging, cache, compressão | Lazy loading, autenticação |
A semelhança estrutural é que ambos implementam a mesma interface do alvo, mas a diferença de intenção é crucial: o Decorator modifica comportamento interno, enquanto o Proxy gerencia o contexto externo.
7. Boas Práticas e Armadilhas Comuns
Armadilhas do Decorator:
- Complexidade excessiva de empilhamento (mais de 5 camadas)
- Dependência da ordem de composição
- Dificuldade de debug com muitas camadas
Armadilhas do Proxy:
- Vazamento da identidade do objeto real (não expor referências internas)
- Overhead desnecessário se o objeto real for leve
- Problemas de concorrência em proxies compartilhados
Boas práticas:
- Manter decorators pequenos e focados em uma única responsabilidade
- Usar injeção de dependência para gerenciar a composição
- Testar cada camada isoladamente com mocks
8. Considerações Finais e Integração na Série
O Decorator e o Proxy são ferramentas poderosas na arquitetura de software, cada um com seu propósito específico. Enquanto o Decorator adiciona funcionalidades de forma incremental, o Proxy gerencia o acesso e o ciclo de vida.
Em um cenário unificado, podemos combiná-los:
// Serviço com cache (proxy) e logging (decorator)
IService service = new LoggingDecorator(
new CacheProxy(
new RealService()
)
);
service.Execute(); // Cache controla acesso ao real service; logging registra chamadas
A escolha entre eles depende do propósito arquitetural: use Decorator quando quiser adicionar comportamento, e Proxy quando precisar controlar acesso ou gerenciar recursos.
Referências
- Refactoring Guru: Decorator Pattern — Explicação detalhada com diagramas UML e exemplos em múltiplas linguagens
- Refactoring Guru: Proxy Pattern — Guia completo sobre os tipos de proxy e suas aplicações práticas
- Microsoft Docs: Decorator Pattern in .NET — Implementação oficial do padrão Decorator com exemplos em C#
- SourceMaking: Proxy Design Pattern — Tutorial detalhado com exemplos de proxy virtual, de proteção e remoto
- Arquitetura de Software: Padrões Estruturais — Capítulo sobre padrões estruturais do livro Head First Design Patterns (O'Reilly)
- DZone: Decorator vs Proxy — Artigo comparativo com exemplos práticos e análises de desempenho
- Martin Fowler: Patterns of Enterprise Application Architecture — Referência clássica sobre padrões arquiteturais, incluindo proxy e decorator em sistemas enterprise