O que é? Como e Por quê usar? – Decorator Pattern

Rá! Acá estou eu para explicar um pouco mais sobre o Decorator pattern, um deles que eu acho pouco usado, mas pode salvar vidas. Tem poder na mão. Alguns podem já ter noção do que seja, mas outros devem estar se perguntando: ‘WTF??’. Então vamos lá:

Decorator Pattern

É um pattern desenvolvido para modificar um determinado objeto (seja modificar alguma coisa já existente ou delegar uma nova responsabilidade) sem a necessidade de extendê-lo. O que é muito bom, assim, o reaproveitamento de código é maior ainda! Enfim, vou tentar dar uma explicada no pattern:

Diagrama de Classes do Decorator Pattern

Diagrama de Classes do Decorator Pattern

Abaixo segue o código usado:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
class Message {
 
	protected $msg;
 
	public function __construct($msg) {
		$this->setMsg($msg);
	}
 
	public function getMsg() {
		return $this->msg;
	}
 
	public function setMsg($msg) {
		$this->msg = $msg;
	}
 
	public function __toString() {
		return $this->getMsg();
	}
 
}
 
class Success_Message extends Message {
	public function __construct($msg) {
		parent::__construct($msg);
		$msg = new DivClass_Decorator(
			new Li_Decorator(
				$this
			)
			,'success-msg');
		$this->setMsg((String)$msg);
	}
}
 
class Error_Message extends Message {
 
	public function __construct($msg) {
		parent::__construct($msg);
		$newmsg = new DivClass_Decorator(
			new Li_Decorator(
				$this
			)
			,'error-msg');
		$this->setMsg((String)$newmsg);
	}
 
}
 
interface Msg_Decorator_Interface {
	function render(Message $message);
}
 
abstract class Msg_Decorator_Abstract extends Message {
 
	public function __construct(Message $msg) {
		$this->render($msg);
	}
 
}
 
class Li_Decorator extends Msg_Decorator_Abstract implements Msg_Decorator_Interface {
 
	public function render(Message $message) {
		$msg = $message->getMsg();
		$this->setMsg('<ul><li>'. $msg .'</li></ul>');
	}
 
}
 
class Div_Decorator extends Msg_Decorator_Abstract implements Msg_Decorator_Interface {
 
	public function render(Message $message) {
		$this->setMsg('<div>'. $message->getMsg() .'</div>');
	}
 
}
 
class DivClass_Decorator extends Msg_Decorator_Abstract implements Msg_Decorator_Interface {
 
	private $class;
 
	public function __construct(Message $msg, $class = null) {
		$this->class = $class;
		parent::__construct($msg);
	}
 
	public function render(Message $message) {
		$this->setMsg('<div class="'.(null === $this->class?'default-class':$this->class).'">'.$message->getMsg().'</div>');
	}
 
}

OK! Com o código em mãos, vamos pensar um pouquinho. Tenho uma cacetada de classes, todas fazendo praticamente a mesma coisa. E agora? Calma, Juvenal. Vamos analizar:

  1. Temos a classe Mensagem. Sabemos que ele representa uma mensagem. Métodos básicos para manipularmos a mensagem e um __toString() que irá retornar o valor em string pra classe quando for feito o cast.
  2. Classe Msg_Decorator_Abstract que extende da classe Mensagem. Todos os nossos decoradores, por assim dizer, irão ser filhos desta classe. O exemplo é tão simples que a classe abstrata nem tem métodos abstratos. Mas a idéia é que tenha, caso necessário.
  3. Msg_Decorator_Interface, essa interface obriga os programadores que quiserem criar decorators a escrever o método render(). Que é chamado pela nossa classe Abstract no construtor.
  4. Li_Decorator, Div_Decorator e DivClass_Decorator são exemplos de decorators.

Vamos ver o negócio funcionando? Vamos escrever o seguinte código:

1
2
3
4
5
   	print new Div_Decorator(
			new Li_Decorator(
				new Message("Mensagem de teste!")
			)
		);

Irá imprimir

1
2
3
4
5
<div>
<ul>
	<li>Mensagem de teste!</li>
</ul>
</div>

Viram? Simples e prático! Claro que o exemplo é bem simples e talvez nem seja tão útil. Mas é só pensar um pouquinho mais que logo surgem exemplos onde se usar o Decorator.

Entender agora fica mais fácil. Apenas comece olhando do meio para a esquerda. Primeiro nós instanciamos a classe Message com o mensagem “Mensagem de teste!”. O método getMsg() retornará exatamente isso: “Mensagem de teste!”. Porém, no momento em que é instanciado o Li_Decorator, ele vai mudar a mensagem usando o método render(), que deve ser implementado na Msg_Decorator_Interface e chamado na Msg_Decorator_Abstract pelo construtor. E assim acontece com o Div_Decorator.

Notem que eu decidi mudar o valor da mensagem usando o setMsg. Eu já li em alguns lugares onde fica armazenada a instância da classe anterior e então o método getMsg seria chamado em forma de cascata, fazendo com que a mensagem original nunca fosse alterada de verdade, e no fim ela é, igual a um método recursivo, retornada decorada e maquiada.

Reutilizando o código

Bom, como eu havia dito no começo, o Observer Pattern nos possibilita o reuso. O exemplo são aquelas duas classes das quais eu não cheguei a falar: Error_Message e Success_Message.

Observem que são simples e, utilizando os Decorators já existentes, mostram uma mensagem personalizada de Erro e de Sucesso! Para utilizá-las, basta usar:

1
2
print new Error_Message("Erro! Você fez algo errado!");
print new Success_Message("Sucesso! Parabéns!");

O que imprimirá:

1
2
3
4
5
6
7
8
9
10
<div class="error-msg">
<ul>
	<li>Error!</li>
</ul>
</div>
<div class="success-msg">
<ul>
	<li>Sucesso! Parabéns!</li>
</ul>
</div>

Prós

Contras

Acho que é isto pessoal. O que acharam? Não é um bixo de sete cabeças, né?

Posts Relacionados

Por favor, se você deseja opinar, criticar ou até mesmo mandar uma receita de bolo, deixe um comentário! Ou fique atento aos feeds.

Comentários

Daee Mick!

Não entedi nada, mas parece bonito!

Parabens, agora vou ler o do g-VIm

Hey man!!!
Massa o Post,com certeza irei utilizar nas minhas aplicações!!
Salamaleico….

Deixe um comentário

(obrigatório)

(obrigatório)