Decorator
Introdução
- No editor de documentos WYSIWYG, vamos embelezar a interface do usuário (UI)
- Vamos adicionar uma borda à área de edição
- Vamos adicionar barras de rolagem (scroll bars)
- Não vamos usar herança para adicionar este embelezamento
- Não poderíamos mudar o embelezamento em tempo de execução
- Para n tipos de embelezamento, precisaríamos de 2n-1 subclasses para ter
todas as combinações
- Teremos mais flexibilidade se outros objetos da UI não souberem que está havendo
embelezamento!
Inclusão transparente
- Usaremos composição em vez de herança evita os problemas mencionados, mas quais
objetos devem participar da composição?
- O embelezamento em si será um objeto (digamos uma instância da classe Borda)
- Isso nos dá dois objetos para fazer a composição: ElementoDeDocumento e Borda
- Devemos decidir agora quem vai compor quem
- A Borda pode conter o ElementoDeDocumento
- Faz sentido já que a Borda engloba o ElementoDeDocumento na tela
- O ElementoDeDocumento pode conter a Borda
- Isso implica em mudar a classe ElementoDeDocumento para que ela conheça a Borda
- Usaremos a primeira escolha de forma a manter o código que trata bordas inteiramente na
classe Borda sem mexer nas outras classes
- Como construir a classe Borda?
- O fato da borda ter uma aparência na tela sugere que ela deveria ser uma subclasse de
ElementoDeDocumento
- Tem um outro motivo mais forte ainda de fazer isso: clientes tratam objetos
ElementoDeDocumento e não deveriam saber se um ElementoDeDocumento tem uma Borda ou não!
- Clientes devem tratar ElementoDeDocumento uniformemente
- Se um cliente manda um ElementoDeDocumento sem Borda se desenhar não vai aparecer
borda, caso contrário, vai aparecer borda, mas o cliente não sabe
- Isso implica que a Borda tem a mesma interface que ElementoDeDocumento
- Fazemos Borda uma subclasse de ElementoDeDocumento para garantir este relacionamento
- Este conceito chama-se "Inclusão Transparente"
- Composição com um único filho; e
- Interfaces compatíveis
- Clientes não sabem se estão tratando do objeto original (ElementoDeDocumento) ou do
seu incluidor (Borda)
- O incluidor delega as operações para o objeto incluído, mas aumenta o comportamento
fazendo seu trabalho antes ou depois da delegação da operação
A classe MonoElementoDeDocumento
- Definimos uma subclasse de ElementoDeDocumento chamada MonoElementoDeDocumento
- MonoElementoDeDocumento é uma classe abstrata para todos os ElementoDeDocumento de
embelezamento

- MonoElementoDeDocumento armazena uma referência a um único ElementoDeDocumento (daí
"mono") e encaminha todos os pedidos para ele
- Isso é chamado Forwarding ou Delegação
- Isso faz com que MonoElementoDeDocumento seja completamente transparente aos clientes
- A classe MonoElementoDeDocumento vai reimplementar pelo menos um dos métodos de
ElementoDeDocumento para fazer seu trabalho
- Exemplo: Borda redefine o método draw():
public class Borda extends MonoElementoDeDocumento {
...
public void draw(Component c) {
super.draw(c);
drawBorda(c);
}
...
}
- Observe que Borda estende o método do pai para desenhar a borda
- Já que chama o método do pai antes de fazer seu trabalho
- A classe Rolador também é um embelezamento
- Ela desenha seu Componente em posições diferentes dependendo de duas barras de rolagem
- Ela adiciona as barras de rolagem como embelezamento
- Ao desenhar seu Componente, Rolador pede ao sistema gráfico para fazer
"clipping" do Componente nos limites apropriados
- As partes fora dos limites não aparecerão
- Agora, como adicionar uma Borda e um Rolador?
- Compomos os ElementosAFormatar dentro de um Rolador e este dentro de uma Borda
- Se compuséssemos a Borda dentro do Rolador, a borda seria rolada também
- Se isso for o comportamento desejado, essa ordem deve ser usada
- A estrutura de objetos aparece abaixo

- Observe que os embelezadores compõem um único Componente
- Se tentássemos embelezar mais de uma coisa de cada vez, teríamos que misturar vários
tipos de ElementoDeDocumento com embelezadores
- Teríamos embelezamento de linha, embelezamento de coluna, etc.
- O jeito que fizemos parece melhor
- Este padrão chama-se Decorator
- Embelezamento usando inclusão transparente
- Observe que o embelezamento pode, na realidade, ser qualquer adição de
responsabilidades e não um "embelezamento visual"
- Ex. embeleza uma árvore de sintaxe com ações semânticas
- Ex. embeleza uma máquina de estados finitos com novas transições
O padrão Decorator
Objetivo
- Adicionar responsabilidades dinamicamente a um objeto
- Decoradores provêem uma alternativa flexível à herança para estender funcionalidade
- Permitem adicionar responsabilidades a um objeto e não a uma classe inteira
Também chamado de
Quando usar o padrão Decorator?
- Para adicionar responsabilidades dinamicamente a objetos individuais e transparentemente
(sem afetar outros objetos)
- Quando há responsabilidades que podem ser retiradas
- Quando herança geraria uma explosão de subclasses
- Quando a herança seria uma boa alternativa mas a definição da classe está escondida
ou não disponível para herança
Estrutura genérica

Participantes
- ComponenteIF (ex. ElementoDeDocumentoIF)
- Define a interface de objetos que podem ter responsabilidades adicionadas dinamicamente
- Componente (ex. ElementoDeDocumento)
- Possível classe abstrata para fatorar o código comum dos ComponenteConcreto
- ComponenteConcreto (ex. ElementosAFormatar)
- Define um objeto ao qual se pode adicionar responsabilidades adicionais
- Decorador (ex. MonoElementoDeDocumento)
- Mantém uma referência a um objeto ComponenteIF e define uma interface igual à do
Componente
- DecoradorConcreto (ex. Borda, Rolador)
- Adiciona responsabilidades ao Componente
Colaborações entre objetos
- O Decorador encaminha pedidos ao Componente
- Pode opcionalmente adicionar operações antes ou depois deste encaminhamento
Consequências do uso do padrão Decorator
- Mais flexível do que herança estática
- Pode adicionar responsabilidades dinamicamente
- Pode até adicionar responsabilidades duas vezes! (ex. borda dupla)
- Evita classes com features demais no topo da hierarquia
- Em vez de tentar prever todos os features numa classe complexa e customizável, permite
definir classes simples e adicionar funcionalidade de forma incremental com objetos
decoradores
- Desta forma, as aplicações não têm que pagar pelos features que não usam
- Ponto negativo: o Decorador e o objeto incluso não têm a mesma identidade
- Portanto, cuidado com a identidade de objetos: um Decorador adiconado a um objeto vai
mudar o objeto com o qual o cliente lida (em termos de identidade)
- Ponto negativo: muitos pequenos objetos
- Fácil de customizar por programadores que entendem o projeto mas pode ser difícil
entender e depurar
Considerações de implementação
- A interface do decorador deve ser igual à interface do objeto sendo decorado
- Se tiver que adicionar uma única responsabilidade, pode remover o decorador abstrato
- Neste caso, o decorador concreto pode tratar do forwarding para o Componente incluso
- Mantendo Componentes enxutos
- A classe Componente deve ser mantida enxuta e os dados ser definidos nos Componentes
concretos
- Caso contrário, os decoradores (que herdam de Componente) ficam "pesados"
Decorator versus Strategy
- Decorator muda a "pele" de um objeto enquanto Strategy muda as
"entranhas"
- Strategy é melhor quando a classe Componente é pesada
- Neste caso, o decorador seria pesado demais
- Com Strategy, parte do comportamento de Componente é repassado para outro objeto (que
implementa um algoritmo)
- O Componente "sabe" da existência do(s) objeto(s) de estratégia mas não
sabe da existência de decoradores
- Exemplo: bordas com Strategy
- O Componente pode mandar um objeto Borda (que ele conhece) desenhar a borda
- O Componente pode manter uma lista de objetos de estratégia para fazer várias coisas,
o que é equivalente a ter decoradores recursivos
- Neste caso, o objeto Borda encapsula a estratégia de desenhar uma borda
- Ver a figura abaixo

Exemplo de código na API Java
- Uso de scroll bars
- ScrollPane() é um decorador que contém um Component e adiciona barras de rolagem
- Não usei Swing para poder mostrar o resultado num applet e vários browsers ainda não
suportam Swing
- Para Swing, a classe é um JScrollPane.
- Exemplo de código segue:
import java.awt.*;
import java.applet.*;
public class Scroll extends Applet {
public void init() {
ScrollPane spane = new ScrollPane();
Button botaoGrande1 =
new Button("Eu adoro Metodos Avançados de Programação!");
botaoGrande1.setFont(new Font("Serif", Font.ITALIC, 80));
Button botaoGrande2 =
new Button("Eu adoro Metodos Avançados de Programação!");
botaoGrande2.setFont(new Font("Serif", Font.ITALIC, 80));
spane.add(botaoGrande1);
add(botaoGrande2); // sem scroll bars
add(spane); // com scroll bars
}
}
Pergunta final para discussão
- Na seção de implementação do Decorator Pattern, os autores (Gamma et al.) escrevem: A
interface de um objeto decorador deve estar de acordo com a interface do Componente que
ele decora.
Agora, considere um objeto A decorado pelo objeto B. Haja vista que o objeto B decora o
objeto A, o objeto B compartilha uma interface com o objeto A. Se algum cliente recebe uma
instância do objeto decorado (B) e tenta chamar um método em B que não faça parte da
interface de A, isto significa que o objeto B não é mais um decorador, no sentido
estrito do padrão? Além do mais, porque é importante que a interface de um objeto
decorador esteja de acordo com a interface do objeto que ele decora?
pat-7 programa anterior
próxima