裝飾者模式的定義
在不必改變原類文件和使用繼承的情況下,動態地擴展一個對象的功能。它是通過創建一個包裝對象,也就是裝飾來包裹真實的對象。
裝飾者模式把每個要裝飾的功能放在單獨的類中,並讓這個類包裝它要裝飾的對象,因此,當需要執行特殊行為時,客戶端代碼就可以在運行的時候根據需要有選擇地、按順序地使用裝飾功能包裝對象了。
圖1
使用場景
設想一下,如果我們需要創建一個在不同場合有不同著裝的學生,例如:在學校學生需要穿上校服,在舞會學生需要穿上正裝,在家學生可以裸裝(有點變態),當然,還可以學習超人把底褲穿在外面。這時候問題來了,難道我們要為每種場合編寫一個不同穿著的學生類嗎?如果我們的童鞋想要一個穿著校服褲子、正裝上衣外露的底褲怎麼辦?StudentWithSchoolUniform、StudentWithFormalWear、StudentWithNaked、StudentWithSchoolUniformAndOutSideUnderWear..................綿綿無盡的類~~~累!是的,如果這樣就造成類爆炸了,需求增加,類就不斷的增加,整個系統的維護難度可想而知。
所以這時候,裝飾者模式就可以發揮它的作用了,底褲、正裝、校服、鞋子、眼鏡等等都是具體的裝飾者,學生是具體的被裝飾的對象,被裝飾的對象和裝飾者的抽象類都繼承者同一個父類。為學生穿上不同的服裝,其實就是使用裝飾者類(服裝)包裹被裝飾者類(學生),形象的說這是一個穿衣的過程。
類和接口
例子
圖2
Person.php

Student.php

Costume.php

Shirt.php

Pants.php

Glasses.php

UnderWear.php

Client.php
1 <?php
2
3 require_once 'Person.php';
4 require_once 'Costume.php';
5 require_once 'Student.php';
6 require_once 'UnderWear.php';
7 require_once 'Shirt.php';
8 require_once 'Pants.php';
9 require_once 'Glasses.php';
10
11 // Student繼承Person
12 $jc = new Student('JC');
13 $jc->show(); // 我是學生JC
14 echo '<br>';
15
16 // 用UnderWear類裝飾Person
17 $underwear = new UnderWear($jc);
18 $underwear->show(); // 我是學生JC,穿著DK
19 echo '<br>';
20
21 // 再用Pants類裝飾Person
22 $pants = new Pants($underwear);
23 $pants->show(); // 我是學生JC,穿著DK,穿著褲子
24 echo '<br>';
25
26 // 再用Shirt類裝飾Person
27 $shirt = new Shirt($pants);
28 $shirt->show(); // 我是學生JC,穿著DK,穿著褲子,穿著襯衫
29 echo '<br>';
30
31 // 再用Glasses類裝飾Person
32 $glasses = new Glasses($shirt);
33 $glasses->show(); // 我是學生JC,穿著DK,穿著褲子,穿著襯衫,帶著眼鏡
34 echo '<br>';
圖3 輸出結果截圖
Symfony2 EventDispatch 組件對裝飾者模式的應用
圖4 Symfony2 EventDispatch組件使用裝飾模式

圖5 Framework配置EventDispatcher
具體裝飾者對象Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher::dispatch()方法,核心依舊是調用被裝飾的具體對象Symfony\Component\EventDispatcher\EventDispatcher::dispatch()方法進行工作,但是裝飾者對象Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher::dispatch()方法添加了相應的功能,例如在調用Symfony\Component\EventDispatcher\EventDispatcher::dispatch()方法前後分別調用了preProcess()、preDispatch()和postDispatch()、postProcess():
1 /**
2 * {@inheritdoc}
3 */
4 public function dispatch($eventName, Event $event = null)
5 {
6 if (null === $event) {
7 $event = new Event();
8 }
9
10 // 裝飾者對象增加的功能
11 $this->preProcess($eventName);
12 $this->preDispatch($eventName, $event);
13
14 $e = $this->stopwatch->start($eventName, 'section');
15
16 // 核心依舊是調用被裝飾的具體對象Symfony\Component\EventDispatcher\EventDispatcher::dispatch()方法
17 $this->dispatcher->dispatch($eventName, $event);
18
19 if ($e->isStarted()) {
20 $e->stop();
21 }
22
23 // 裝飾者對象增加的功能
24 $this->postDispatch($eventName, $event);
25 $this->postProcess($eventName);
26
27 return $event;
28 }
優點
缺點