Decorator
O design pattern Decorator trata de uma maneira de adicionar novas capacidades aos métodos de um objeto dinamicamente utilizando uma espécie de proxy, geralmente implementado através da passagem do objeto original como parâmetro ao construtor do Decorator, que é responsável por implementar a nova funcionalidade, e por chamar os métodos do objeto original após execução dos seus métodos correspondentes.
Vamos tentar entender isso melhor com um exemplo concreto, o exemplo clássico da janela com bordas e scrollbars.
Imagine um componente que renderiza texto, digamos, TextView, com o seu método draw responsável por desenhá-lo na tela. Este componente pode assumir diferentes aparências em um dado sistema: pode ser apresentado da sua forma original, pode ter bordas mais grossas, pode ter scrollbars ou ainda scrollbars e bordas grossas.
Se utilizarmos a abordagem da herança, para cada combinação de funcionalidades precisaríamos de uma nova subclasse de TextView, algo como BorderedTextView, ScrollabeTextView e um ScrollabeBorderedTextView. Não é difícil notar que para cada funcionalidade adicionada o número de combinações se multiplica.
Ao invés de utilizarmos a abordagem da herança, utilizamos o design pattern Decorator. Para isso precisamos de uma interface Component que define o método draw.
O TextView implementa esta interface e cada um dos decorators, objetos responsáveis por adicionar as novas funcionalidades ao TextView também. Para não deixar o exemplo muito abstrato, vale citar que teríamos para o nosso exemplo objetos como TextViewScrollDecorator e TextViewBorderDecorator .
A engenhosidade do pattern fica por conta dos construtores dos decorators, que recebe como referência um Component e guarda essa referência. Quando um decorator tem o seu método draw invocado ele se precupa em executar o código responsável pela nova funcionalidade (exemplo: desenhar uma borda) e por chamar o método draw do objeto original.
Note que utilizando esse pattern podemos fazer qualquer combinação de funcionalidades. Observe o código abaixo:
(...)
// cria um textView normal
Component tView = new TextView();
// cria um textView com scroll
Component scrollabeTView = new TextViewScrollDecorator(new TextView());
// cria um textview com scroll e bordas
Component scrollableBorderedTView =
new TextViewBorderDecorator(new TextViewScrollDecorator(new TextView()));
(...)
No caso do Component ter vários métodos, faz mais sentido prover uma classe abstrata Decorator que implementa a interface Component e define o construtor padrão que recebe o Component como parâmetro e guarda uma referência dele. Finalmente essa classe abstrata Decorator pode definir para cada método presente na interface Component uma implementação padrão que simplesmente repasse as chamadas aos métodos correspondentes do seu Component recebido no construtor.