先從裝飾者模式的由來來講起
如果我們要做一個遊戲中的角色,要添加武器,鞋子,衣服等等,最簡單的設計方法
方案1:
class Hero
{
private:
string m_name;
public:
Hero(string name):m_name(name){cout<<m_name;}
void WearShirt(); //穿衣服
void WearWeapons(); //裝備武器
void WearShoes(); //穿鞋子
/*等等的方法*/
};
所以調用起來這樣
Hero obj("li");
obj.WearShirt();
obj.WearWeapons();
obj.WearShoes();
總結這個方法:如果我要加一個之前沒想過的操作的話,我就得直接到類裏面去改動,這已經違背了開放-封閉的原則(對於擴展是開放的,但是更改應該是閉合的),這樣重用性很差,稍微大一點的項目就容易亂套。
既然這樣不好,我們應該把武器,鞋子這些抽象成一個抽象類,這些用不同的裝備去繼承它,所以
方案2:
class Hero
{
private:
string m_name;
public:
Hero(string name):m_name(name){cout<<m_name;}
};
class Equipment //裝備的抽象接口
{
public:
virtual void Euip() = 0;
};
/*下面爲子類*/
class Shirt : public Equipment //襯衣
{
public:
virtual void Euip();
};
void Shirt :: Euip()
{
cout << "+穿襯衫";
}
class Weapons : public Equipment //武器
{
public:
virtual void Euip();
};
void Weapons ::Euip()
{
cout << "+裝備武器";
}
class Shoes : public Equipment //鞋子
{
public:
virtual void Euip();
};
void Shoes ::Euip()
{
cout << "+穿鞋子";
}
這樣調用的話
Hero obj("LI");
Equipment *pshirt = new Shirt;
Equipment *pweapons = new Weapons;
Equipment *pshoes = new Shoes;
pshirt->Euip();
pweapons->Euip();
pshoes->Euip();
這裏的感覺上是對了,但是這樣的寫法是有問題的,現在的做法就像是一件件武器顯示出來,他的裝備是在大家面前一步步把武器,鞋子,襯衫穿上,很不符合常理,正常的應該是再類的內部穿好再展示出來。所以下面有了裝飾模式,聽聽名字就應該很合適
先給出類設計圖
這個整個的類圖,
再上代碼
class Component //所有的總類
{
public:
virtual void Euip() = 0;
};
class Hero : public Component
{
private:
string m_name;
public:
Hero(string name):m_name(name){cout<<m_name;}
virtual void Euip();
};
void Hero::Euip()
{
cout << "LI";
}
//裝備的抽象接口
class EquipDecorator : public Component
{
private:
Component *pcomponet;
public:
virtual void Euip();
void Decprate(Component *p)
{
this->pcomponet = p;
}
};
void EquipDecorator::Euip()
{
if(pcomponet != NULL)
{
pcomponet->Euip();
}
}
/*下面爲子類*/
class Shirt : public EquipDecorator //襯衣
{
private:
string m_color;
public:
Shirt(string color):m_color(color){};
virtual void Euip();
};
void Shirt :: Euip()
{
EquipDecorator::Euip(); //先讓之前的裝備裝起來
cout << "+穿" << m_color << "的襯衫"; //執行子類動作
}
class Weapons : public EquipDecorator //武器
{
private:
string m_length;
public:
Weapons(string length):m_length(length){};
virtual void Euip();
};
void Weapons ::Euip()
{
EquipDecorator::Euip(); //先讓之前的裝備裝起來
cout << "+拿" << m_length << "米長的武器"; //執行子類動作
}
class Shoes : public EquipDecorator //鞋子
{
private:
string m_speed;
public:
Shoes(string speed):m_speed(speed){};
virtual void Euip();
};
void Shoes ::Euip()
{
EquipDecorator::Euip(); //先讓之前的裝備裝起來
cout << "+穿速度爲" << m_speed << "m/s的鞋子"; //執行子類動作
}
一口氣看完,有點沒看懂沒關係,等一下再解釋,所以客戶端的調用是這樣的
//創建基本對象
Hero person("LI");
Shirt shirt("白色");
Weapons weapons("100");
Shoes shoes("20");
shirt.Decprate(&person); //裝飾主角
weapons.Decprate(&shirt); //裝飾裝好shirt主角
shoes.Decprate(&weapons); //裝飾裝好shirt和武器的主角
shoes.Euip(); //顯示出來
結果
這個時候肯定有點蒙,所以最好的是理清思路,跟着代碼走一遍
Hero person("LI");
Shirt shirt("白色");
Weapons weapons("100");
Shoes shoes("20");
這裏創建主要注意的就是要先構造他自己的基類的比較簡單
接下來是
shirt.Decprate(&person);//裝飾主角
weapons.Decprate(&shirt); //裝飾裝好shirt主角
shoes.Decprate(&weapons); //裝飾裝好shirt和武器的主角
這個是重點了。
shoes.Euip();
流程爲:
1. shoes.Euip();
2. EquipDecorator::Euip();
3. pcomponet->Euip();(pcompnet爲weapons的指針,在之前的Decprate()保存下來的)
4. 繼續執行2.
5. 執行3.pcomponet->Euip();(pcompnet爲shirt的指針)
6. 繼續執行2.
7. 執行3.pcomponet->Euip();(pcompnet爲Hero的指針)
8. 調用到了終點hero的Euip
void Hero::Euip()
{
cout<<m_name;
}
9. 結束7的調用,回到shirt的Euip
void Shirt :: Euip()
{
//已經執行了 EquipDecorator::Euip();
cout << "+穿" << m_color << "的襯衫"; //執行子類動作
}
...
再一層層的向回走,把所有的添加的都輸出一遍
好處和作用引用一下別人的
裝飾者模式
Decorator模式(別名Wrapper):動態將職責附加到對象上,若要擴展功能,裝飾者提供了比繼承更具彈性的代替方案。
意圖:
動態地給一個對象添加一些額外的職責。就增加功能來說,Decorator模式相比生成子類更爲靈活。
設計原則:
1. 多用組合,少用繼承。
利用繼承設計子類的行爲,是在編譯時靜態決定的,而且所有的子類都會繼承到相同的行爲。然而,如果能夠利用組合的做法擴展對象的行爲,就可以在運行時動態地進行擴展。
2. 類應設計的對擴展開放,對修改關閉。
要點:
1. 裝飾者和被裝飾對象有相同的超類型。
2. 可以用一個或多個裝飾者包裝一個對象。
3. 裝飾者可以在所委託被裝飾者的行爲之前或之後,加上自己的行爲,以達到特定的目的。
4. 對象可以在任何時候被裝飾,所以可以在運行時動態的,不限量的用你喜歡的裝飾者來裝飾對象。
5. 裝飾模式中使用繼承的關鍵是想達到裝飾者和被裝飾對象的類型匹配,而不是獲得其行爲。
6. 裝飾者一般對組件的客戶是透明的,除非客戶程序依賴於組件的具體類型。在實際項目中可以根據需要爲裝飾者添加新的行爲,做到“半透明”裝飾者。
7. 適配器模式的用意是改變對象的接口而不一定改變對象的性能,而裝飾模式的用意是保持接口並增加對象的職責。