案例
黑棗芭比公司是主營芭比娃娃遊戲的公司,主要生産芭比娃娃,資訊記錄了一個芭比娃娃裝備及美麗星級指數,可以看到,一個小眼睛扁鼻子闊嘴的芭比娃娃值0個指數。
<實作代碼>
class Barbie
{
protected $eye = 'small';
protected $nose = 'flat';
protected $mouth = 'wide';
public $star = 0;
public function getInfo()
{
echo "Barbie Info\n";
echo " eye:" . $this->eye."\n";
echo " nose:" . $this->nose . "\n";
echo " mouth:" . $this->mouth . "\n";
}
public function setStar()
{
if ($this->eye == 'big') $this->star++;
if ($this->nose == 'stand') $this->star++;
if ($this->mouth == 'small') $this->star++;
}
public function getStar()
{
echo " Star:" . $this->star . "\n";
}
}
$ba = new Barbie();
$ba->getInfo();
$ba->getStar();
<系統要更新>
芭比娃娃可不是一成不變的,随着幹爹之類的盛行,芭比猶如白富美一樣不僅僅從眼睛鼻子嘴上下工夫,皮膚白加2星,腿長加3星,手細加1星,脖子細加1星。。。
<代碼重構>
class Barbie
{
protected $eye = 'big';
protected $nose = 'flat';
protected $mouth = 'wide';
protected $skin = 'black';
protected $leg = 'short';
protected $hand = 'rough';
protected $neck = 'short';
public $star = 0;
public function getInfo()
{
echo "Barbie Info\n";
echo " eye:" . $this->eye . "\n";
echo " nose:" . $this->nose . "\n";
echo " mouth:" . $this->mouth . "\n";
}
public function setStar()
{
if ($this->eye == 'big') $this->star++;
if ($this->nose == 'stand') $this->star++;
if ($this->mouth == 'small') $this->star++;
if ($this->skin == 'white') $this->star += 2;
//省略中。。。
}
public function getStar(){
echo " Star:" . $this->star . "\n";
}
}
$ba = new Barbie();
$ba->getInfo();
$ba->getStar();
<需求更新變更>
芭比眼睛鼻子嘴皮膚腿手各分為18種類型,每個類型都有不同的星級,如一個打扮了大鼻子芭比娃娃可要扣2個星級。
如果這樣下去,Barbie類可能超過5000行代碼,而且這個龐大的類也很難維護更新。
程式猿于是已經崩潰
分析OOA:
為什麼Barbie類代碼會越寫越多,因為它違背我們的設計原則:類應該對擴充開放,對修改關閉。我們的目标是允許類容易擴充。在不修改現有代碼的基礎上,就可以搭配新的行為。這樣設計才可以接受新的功能來應對改變的需求
設計OOD:
<UML>
<說明>
1.Barbie為被裝飾者。
2.NoseDecorator、MouthDecorator等類為裝飾者,繼承了Barbie并包含了Barbie的一個私有執行個體個兩個公共方法。
3.Decorator類的構造方法,接收一個Barbie類型并将其存儲在内部。
程式設計 OOP:
<代碼>
class Barbie{
public function getInfo(){
echo "Barbie Info\n";
}
public function getStar(){
return 0;
}
}
class EyeDecorator extends Barbie {
private $barbie;
public function __construct(Barbie $barbie){
$this->barbie = $barbie;
}
public function getInfo(){
$this->barbie->getInfo();
echo " Eye:big\n";
}
public function getStar(){
return $this->barbie->getStar() + 2;
}
}
class NoseDecorator extends Barbie
{
private $barbie;
public function __construct(Barbie $barbie)
{
$this->barbie = $barbie;
}
public function getInfo()
{
$this->barbie->getInfo();
echo " Nose:small\n";
}
public function getStar()
{
return $this->barbie->getStar() + 1;
}
}
class MouthDecorator extends Barbie
{
private $barbie;
public function __construct(Barbie $barbie)
{
$this->barbie = $barbie;
}
public function getInfo()
{
$this->barbie->getInfo();
echo " Mouth:small\n";
}
public function getStar()
{
return $this->barbie->getStar() + 2;
}
}
class PlushskinDecorator extends Barbie
{
private $barbie;
public function __construct(Barbie $barbie)
{
$this->barbie = $barbie;
}
public function getInfo()
{
$this->barbie->getInfo();
echo " skin:Plush\n";
}
public function getStar()
{
return $this->barbie->getStar() - 10;
}
}
測試用例Test Case:
<代碼>
class testDriver
{
public function run()
{
//建立一個高鼻梁大眼睛小嘴巴的芭比
$ba = new MouthDecorator(new EyeDecorator(new NoseDecorator(new Barbie())));
$ba->getInfo();
echo $ba->getStar();
//再給這個芭比加些毛
$ba = new PlushskinDecorator($ba);
$ba->getInfo();
echo $ba->getStar();
}
}
$test = new testDriver();
$test->run();
<輸出>
小結:
1. 裝飾者和被裝飾對象有相同的超類型。
2. 可以用一個或多個裝飾者包裝一個對象。
3. 裝飾者可以在所委托被裝飾者的行為之前或之後,加上自己的行為,以達到特定的目的。
4. 對象可以在任何時候被裝飾,是以可以在運作時動态的,不限量的用你喜歡的裝飾者來裝飾對象。
5. 裝飾模式中使用繼承的關鍵是想達到裝飾者和被裝飾對象的類型比對,而不是獲得其行為。
6. 裝飾者一般對元件的客戶是透明的,除非客戶程式依賴于元件的具體類型。在實際項目中可以根據需要為裝飾者添加新的行為,做到“半透明”裝飾者。
7. 擴充卡模式的用意是改變對象的接口而不一定改變對象的性能,而裝飾模式的用意是保持接口并增加對象的職責。
********************************************
* 作者:葉文濤
* 标題:Php設計模式之裝飾者模式
* 時間:2012-5-15
* 參考:
*《PHP設計模式》(美)Aaron Saray著
*《PHP進階程式設計模式、架構與測試》(加)Kevin McArthur著
*《Head First設計模式》Eric Freeman等著
******************轉載請注明來源 ***************