加入星計(jì)劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴(kuò)散
  • 作品版權(quán)保護(hù)
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長(zhǎng)期合作伙伴
立即加入
  • 正文
    • 1 裝飾器模式
    • 2 穿搭衣服實(shí)例
    • 3 總結(jié)
  • 推薦器件
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

《大話設(shè)計(jì)模式》解讀03-裝飾模式

06/24 10:45
393
閱讀需 15 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

本篇文章,來(lái)解讀《大話設(shè)計(jì)模式》的第6章——裝飾模式。并通過(guò)C++代碼實(shí)現(xiàn)實(shí)例代碼的功能。

注:第3~5章講的是設(shè)計(jì)模式中的一些原則(第3章:?jiǎn)我宦氊?zé)原則;第4章:開(kāi)放-封閉原則;第5章:依賴倒轉(zhuǎn)原則和里氏替換原則),這些原則在設(shè)計(jì)模式中用到時(shí)會(huì)提及,暫不做專門(mén)解讀。

1 裝飾器模式

裝飾模式,或稱裝飾器模式(Decorator),動(dòng)態(tài)地給一個(gè)對(duì)象添加一些額外的職責(zé),就增加功能來(lái)說(shuō),裝飾模式比生成子類更加靈活

我們?cè)诮o對(duì)象增加功能時(shí),一種做法是再設(shè)計(jì)一個(gè)子類來(lái)繼續(xù)父類,然后給子類添加額外的功能,另一種做法就是通過(guò)裝飾模式,設(shè)計(jì)單獨(dú)用于裝飾功能的類,通過(guò)“包裝”的形式動(dòng)態(tài)給某個(gè)對(duì)象增加功能。

2 穿搭衣服實(shí)例

題目:用控制臺(tái)程序,寫(xiě)可以給人搭配衣服的代碼

2.1 版本一

版本一的代碼,僅定義了一個(gè)Person類,提供6種不同服飾的裝扮接口,以及1個(gè)名字展示接口。

2.1.1 Person類

Person類的代碼如下,維護(hù)一個(gè)人的名字,然后是6種服飾的穿衣接口,就是加一句打印,最后Show接口顯示人的名字。

class Person
{
public:
    Person(std::string name)
    {
        m_name = name;
    }
    
    void WearTShirts()
    {
        printf("大T恤 ");
    }
    
    void WearBigTrouser()
    {
        printf("垮褲 ");
    }
    
    void WearSneakrs()
    {
        printf("破球鞋 ");
    }
    
    void WearSuit()
    {
        printf("西裝 ");
    }
    
    void WearTie()
    {
        printf("領(lǐng)帶 ");
    }
    
    void WearLeatherShoes()
    {
        printf("皮鞋 ");
    }
    
    void Show()
    {
        printf("裝扮的%s", m_name.c_str());
    }  
             
private:
    std::string m_name;
};

2.1.2 主函數(shù)

主函數(shù)的邏輯如下,先實(shí)例化一個(gè)名為"小菜"的Person對(duì)象,然后依次調(diào)用穿衣接口,最后調(diào)用展示接口:

#include <iostream>

int main()
{
    Person xc = Person("小菜");
    
    printf("n第一種裝扮:");
    xc.WearTShirts();
    xc.WearBigTrouser();
    xc.WearSneakrs();
    xc.Show();
    
    printf("n第二種裝扮:");
    xc.WearSuit();
    xc.WearTie();
    xc.WearLeatherShoes();
    xc.Show(); 
    
    printf("n");  
    
    return 0;
}

代碼運(yùn)行效果如下:

版本一這種方式,雖然功能實(shí)現(xiàn)了,但如果想要增加裝扮,就需要修改Person類了,這違反了面向?qū)ο笤O(shè)計(jì)中的開(kāi)放-封閉原則。

開(kāi)放-封閉原則:是指軟件實(shí)體(類、模塊、函數(shù)等等)應(yīng)該可以擴(kuò)展,但是不可修改。

換句話說(shuō):

    開(kāi)放:對(duì)擴(kuò)展開(kāi)放,當(dāng)需要增加新功能時(shí),通過(guò)代碼擴(kuò)展的方式(如增加新的類)實(shí)現(xiàn)封閉:對(duì)修改封閉,當(dāng)需要增加新功能時(shí),盡量避免對(duì)原有代碼的修改

下面來(lái)看版本二如何實(shí)現(xiàn)。

2.2 版本二

版本二是將各種服飾單獨(dú)封裝了起來(lái),并繼承自服飾抽象類,將服飾類與人類進(jìn)行了分離,類圖如下:

這樣,后續(xù)需要增加服飾時(shí),只需要增加對(duì)應(yīng)的具體服飾類,而不會(huì)影響其它已有的服飾類的代碼。

2.2.1 Person類與服飾類

Person類與服飾類的代碼如下,Person類只維護(hù)一個(gè)人的名字,并通過(guò)Show接口顯示人的名字。服飾類的Show接口用于顯示服飾的名稱,具體顯示的內(nèi)容由具體服飾類的Show接口實(shí)現(xiàn),也是打印出服飾的名字。

// Person類
class Person
{
public:
    Person(std::string name)
    {
        m_name = name;
    }
    
    void Show()
    {
        printf("裝扮的%s", m_name.c_str());
    }    
      
private:
    std::string m_name;
};

// 服飾類
class Finery
{
public:
    virtual void Show(){};
};

// 各種服飾子類
class TShirts : public Finery
{
public:
    void Show()
    {
        printf("大T恤 ");
    }    
};

class BigTrouser : public Finery
{
public:
    void Show()
    {
        printf("垮褲 ");
    }    
};

class Sneakrs : public Finery
{
public:
    void Show()
    {
        printf("破球鞋 ");
    }    
};

class Suit : public Finery
{
public:
    void Show()
    {
        printf("西裝 ");
    }    
};

class Tie : public Finery
{
public:
    void Show()
    {
        printf("領(lǐng)帶 ");
    }    
};

class LeatherShoes : public Finery
{
public:
    void Show()
    {
        printf("皮鞋 ");
    }    
};

2.2.2 主函數(shù)

主函數(shù)的邏輯如下,先實(shí)例化一個(gè)名為"小菜"的Person對(duì)象,

然后依次實(shí)例化具體要裝扮的服飾類并調(diào)用對(duì)應(yīng)的展示接口,

最后調(diào)用Person的展示接口:

int main()
{
    Person xc = Person("小菜"); //先實(shí)例化一個(gè)名為"小菜"的Person對(duì)象
    
    printf("n第一種裝扮:");
    TShirts dtx; //依次實(shí)例化具體要裝扮的服飾類
    BigTrouser kk;
    Sneakrs pqx;
    dtx.Show(); //調(diào)用對(duì)應(yīng)的展示接口
    kk.Show();
    pqx.Show();
    xc.Show(); //最后調(diào)用Person的展示接口
    
    printf("n第二種裝扮:");
    Suit xz;
    Tie ld;
    LeatherShoes px;
    xz.Show();
    ld.Show();
    px.Show();
    xc.Show(); 
    
    printf("n");  
    
    return 0;
}

代碼運(yùn)行效果如下:

版本二中,Rerson類和服飾類是完全獨(dú)立的,搭配衣服的過(guò)程也只是一個(gè)一個(gè)將對(duì)應(yīng)詞打印出來(lái)。下面來(lái)看版本三。

2.3 版本三

版本三用到了本篇要講的裝飾器模式。

在本例中,服飾就是裝飾類,具體的服飾,T恤、球鞋這些是具體的裝飾類,而人就是要被裝飾的組件,那是組件Component還是具體組件ConcreateComponet呢?

本例中,只有一個(gè)ConcreateComponet具體組件類,就沒(méi)必要單獨(dú)建立一個(gè)Component組件類,可以把兩者職責(zé)合并成一個(gè)類,也就是只使用ConcreateComponet類表示Person類。

2.3.1 Person類與服飾類

Person類與服飾類的代碼如下:

// Person類
class Person
{
public:
    Person(){};
    
    Person(std::string name)
    {
        m_name = name;
    }
    
    // 父類的函數(shù)
    virtual void Show()
    {
        printf("裝扮的%s", m_name.c_str());
    }    
      
private:
    std::string m_name;
};

// 服飾類(Decorator)
class Finery : public Person
{
protected:
    Person *m_pComponent = nullptr;
    
public:
    void Decorate(Person *pComponent)
    {
        m_pComponent = pComponent;
    }
    
    // 子類的函數(shù)
    void Show()
    {
        if (m_pComponent != nullptr)
        {
            m_pComponent->Show();
        }
    }
};

// 具體服飾類(ConcreateDecorator)
class TShirts : public Finery
{
public:
    void Show()
    {
        printf("大T恤 ");
        Finery::Show();
    }   
};

class BigTrouser : public Finery
{
public:
    void Show()
    {
        printf("垮褲 ");
        Finery::Show();
    }    
};

class Sneakrs : public Finery
{
public:
    void Show()
    {
        printf("破球鞋 ");
        Finery::Show();
    }    
};

class Suit : public Finery
{
public:
    void Show()
    {
        printf("西裝 ");
        Finery::Show();
    }    
};

class Tie : public Finery
{
public:
    void Show()
    {
        printf("領(lǐng)帶 ");
        Finery::Show();
    }    
};

class LeatherShoes : public Finery
{
public:
    void Show()
    {
        printf("皮鞋 ");
        Finery::Show();
    }    
};

2.3.2 主函數(shù)

主函數(shù)的邏輯如下,先實(shí)例化一個(gè)名為"小菜"的Person對(duì)象,

然后再依次實(shí)例化要裝扮的服飾類對(duì)象,接著通過(guò)“包裝”的方式,后一個(gè)對(duì)象對(duì)前一個(gè)對(duì)象進(jìn)行包裝,

最后調(diào)用最終包裝后對(duì)象的展示接口:

int main()
{
    Person *xc = new Person("小菜");
    
    printf("n第一種裝扮:");
    TShirts    *dtx = new TShirts();
    BigTrouser *kk  = new BigTrouser(); 
    Sneakrs    *pqx = new Sneakrs();
    
    dtx->Decorate(xc); // 裝飾過(guò)程:先用大褲衩裝飾小菜
    kk->Decorate(dtx); // 再用垮褲裝飾穿了大褲衩的小菜
    pqx->Decorate(kk); // 再用破球鞋裝飾穿了大褲衩和垮褲的小菜
    pqx->Show();       // 最后調(diào)用最外層的裝飾對(duì)象的展示接口

    printf("n第二種裝扮:");
    Suit         *xz = new Suit();
    Tie          *ld = new Tie();
    LeatherShoes *px = new LeatherShoes();
    
    xz->Decorate(xc); // 裝飾過(guò)程:先用西裝裝飾小菜
    ld->Decorate(xz); // 再用領(lǐng)帶裝飾穿了西裝的小菜
    px->Decorate(ld); // 再皮鞋裝飾穿了西裝和領(lǐng)帶的小菜
    px->Show();       // 最后調(diào)用最外層的裝飾對(duì)象的展示接口  

    printf("n");  
    
    return 0;
}

代碼運(yùn)行效果如下:

裝飾模式的優(yōu)缺點(diǎn)與適用場(chǎng)景:

3 總結(jié)

本篇介紹了設(shè)計(jì)模式中的裝飾模式,并通過(guò)給人裝扮的實(shí)例,使用C++編程,來(lái)演示裝飾模式的使用。

推薦器件

更多器件
器件型號(hào) 數(shù)量 器件廠商 器件描述 數(shù)據(jù)手冊(cè) ECAD模型 風(fēng)險(xiǎn)等級(jí) 參考價(jià)格 更多信息
ASEM1-16.000MHZ-LC-T 1 Abracon Corporation MEMS OSC XO 16.0000MHZ CMOS SMD
$1.63 查看
CAT24C04WI-GT3 1 Rochester Electronics LLC 512X8 I2C/2-WIRE SERIAL EEPROM, PDSO8, 0.150 INCH, GREEN, MS-012, SOIC-8
$0.21 查看
LTST-C190CKT 1 Lite-On Semiconductor Corporation Single Color LED, Red, Water Clear, 1.1mm,

ECAD模型

下載ECAD模型
$0.21 查看

相關(guān)推薦

電子產(chǎn)業(yè)圖譜

控制科學(xué)與工程碩士,日常分享單片機(jī)、嵌入式、C/C++、Linux等學(xué)習(xí)經(jīng)驗(yàn)干貨~