Decorator
O problema
Seção intitulada “O problema”Suponha variações combináveis de um comportamento: um relatório pode ser comprimido, assinado digitalmente, com marca-d’água — em qualquer combinação. Por herança, cada combinação vira uma subclasse (RelatorioComprimidoAssinado, RelatorioAssinadoComMarca…): 3 variações já geram 7 classes, e a escolha é fixada em tempo de compilação.
A solução
Seção intitulada “A solução”Uma interface comum, um componente concreto e decorators que a implementam envolvendo outro componente:
public interface GeradorRelatorio { byte[] gerar(DadosRelatorio dados);}
public class GeradorPdf implements GeradorRelatorio {
@Override public byte[] gerar(DadosRelatorio dados) { return renderizarPdf(dados); }}public abstract class RelatorioDecorator implements GeradorRelatorio {
protected final GeradorRelatorio delegado;
protected RelatorioDecorator(GeradorRelatorio delegado) { this.delegado = delegado; }}
public class ComCompressao extends RelatorioDecorator {
public ComCompressao(GeradorRelatorio delegado) { super(delegado); }
@Override public byte[] gerar(DadosRelatorio dados) { return comprimir(delegado.gerar(dados)); }}
public class ComAssinaturaDigital extends RelatorioDecorator {
public ComAssinaturaDigital(GeradorRelatorio delegado) { super(delegado); }
@Override public byte[] gerar(DadosRelatorio dados) { return assinar(delegado.gerar(dados)); }}GeradorRelatorio gerador = new ComAssinaturaDigital( new ComCompressao( new GeradorPdf()));O cliente vê só GeradorRelatorio; combinações novas são composição em runtime, sem nenhuma classe extra — OCP em estado puro. O exemplo canônico da plataforma é o java.io: new BufferedInputStream(new GZIPInputStream(new FileInputStream(...))).
Diagrama
Seção intitulada “Diagrama”classDiagram
class GeradorRelatorio {
<<interface>>
+gerar(DadosRelatorio) byte[]
}
class GeradorPdf {
+gerar(DadosRelatorio) byte[]
}
class RelatorioDecorator {
<<abstract>>
#GeradorRelatorio delegado
}
class ComCompressao
class ComAssinaturaDigital
GeradorRelatorio <|.. GeradorPdf
GeradorRelatorio <|.. RelatorioDecorator
RelatorioDecorator <|-- ComCompressao
RelatorioDecorator <|-- ComAssinaturaDigital
RelatorioDecorator o--> GeradorRelatorio : envolve
Decorator vs Proxy
Seção intitulada “Decorator vs Proxy”As estruturas são idênticas (mesma interface, delegação); a intenção difere. O Proxy controla o acesso a um alvo que ele conhece e geralmente instancia/esconde (lazy, cache, permissão). O Decorator acrescenta comportamento e é empilhável pelo cliente, que monta a cadeia. Regra de bolso: quem decide a composição? Cliente → Decorator; o próprio wrapper → Proxy.
Anti-padrões e erros comuns
Seção intitulada “Anti-padrões e erros comuns”- Ordem implícita:
assinar(comprimir(x))≠comprimir(assinar(x))— quando a ordem dos decorators importa, documente-a ou encapsule a montagem numa factory; deixá-la ao acaso é bug esperando. - Decorator que muda o contrato: filtrar, alterar semântica ou lançar exceções novas viola LSP — o cliente esperava o comportamento da interface.
- Pilha profunda ilegível: dezenas de camadas dificultam debug (stack traces gigantes); considere consolidar.
- Usar onde Strategy bastava: se os comportamentos são alternativos (um ou outro), é Strategy; Decorator é para comportamentos cumulativos.
Relações
Seção intitulada “Relações”- Categoria: Padrões Estruturais · catálogo: Design Patterns (GoF) · Catálogo de Patterns.
- Contraste estrutural: Proxy (mesma forma, intenção diferente) e Facade.
- Realiza OCP - Open-Closed Principle por composição — o princípio “prefira composição à herança” do GoF.
- Comportamentos alternativos (não cumulativos): Strategy.
Perguntas de revisão
Seção intitulada “Perguntas de revisão”1. Que problema o Decorator resolve que a herança não resolve bem?
Resposta
Variações combináveis de comportamento. Por herança, cada combinação vira uma subclasse (explosão combinatória) fixada em compilação; com decorators, combinações são composição em runtime, sem classes novas.
2. Como distinguir Decorator de Proxy, já que a estrutura é a mesma?
Resposta
Pela intenção e por quem monta: o Proxy controla o acesso a um alvo que ele mesmo conhece/esconde; o Decorator acrescenta comportamento e é empilhado pelo cliente, que decide a composição.
3. Por que um decorator que filtra ou altera a semântica é perigoso?
Resposta
Porque quebra a substituibilidade (LSP): o cliente programou contra o contrato da interface, e o decorator entrega menos (ou diferente) do que o contrato promete.
4. Dê um exemplo de Decorator na biblioteca padrão do Java.
Resposta
O pacote java.io: BufferedInputStream, GZIPInputStream etc. envolvem outro InputStream acrescentando buffer, descompressão — todos com a mesma interface, empilháveis em qualquer ordem que faça sentido.