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

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

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

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

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

注:第3~5章講的是設(shè)計模式中的一些原則(第3章:單一職責(zé)原則;第4章:開放-封閉原則;第5章:依賴倒轉(zhuǎn)原則和里氏替換原則),這些原則在設(shè)計模式中用到時會提及,暫不做專門解讀。

1 裝飾器模式

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

我們在給對象增加功能時,一種做法是再設(shè)計一個子類來繼續(xù)父類,然后給子類添加額外的功能,另一種做法就是通過裝飾模式,設(shè)計單獨用于裝飾功能的類,通過“包裝”的形式動態(tài)給某個對象增加功能。

2 穿搭衣服實例

題目:用控制臺程序,寫可以給人搭配衣服的代碼

2.1 版本一

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

2.1.1 Person類

Person類的代碼如下,維護一個人的名字,然后是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ù)的邏輯如下,先實例化一個名為"小菜"的Person對象,然后依次調(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;
}

代碼運行效果如下:

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

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

換句話說:

    開放:對擴展開放,當需要增加新功能時,通過代碼擴展的方式(如增加新的類)實現(xiàn)封閉:對修改封閉,當需要增加新功能時,盡量避免對原有代碼的修改

下面來看版本二如何實現(xiàn)。

2.2 版本二

版本二是將各種服飾單獨封裝了起來,并繼承自服飾抽象類,將服飾類與人類進行了分離,類圖如下:

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

2.2.1 Person類與服飾類

Person類與服飾類的代碼如下,Person類只維護一個人的名字,并通過Show接口顯示人的名字。服飾類的Show接口用于顯示服飾的名稱,具體顯示的內(nèi)容由具體服飾類的Show接口實現(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ù)的邏輯如下,先實例化一個名為"小菜"的Person對象,

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

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

int main()
{
    Person xc = Person("小菜"); //先實例化一個名為"小菜"的Person對象
    
    printf("n第一種裝扮:");
    TShirts dtx; //依次實例化具體要裝扮的服飾類
    BigTrouser kk;
    Sneakrs pqx;
    dtx.Show(); //調(diào)用對應(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;
}

代碼運行效果如下:

版本二中,Rerson類和服飾類是完全獨立的,搭配衣服的過程也只是一個一個將對應(yīng)詞打印出來。下面來看版本三。

2.3 版本三

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

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

本例中,只有一個ConcreateComponet具體組件類,就沒必要單獨建立一個Component組件類,可以把兩者職責(zé)合并成一個類,也就是只使用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ù)的邏輯如下,先實例化一個名為"小菜"的Person對象,

然后再依次實例化要裝扮的服飾類對象,接著通過“包裝”的方式,后一個對象對前一個對象進行包裝,

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

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

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

    printf("n");  
    
    return 0;
}

代碼運行效果如下:

裝飾模式的優(yōu)缺點與適用場景:

3 總結(jié)

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

推薦器件

更多器件
器件型號 數(shù)量 器件廠商 器件描述 數(shù)據(jù)手冊 ECAD模型 風(fēng)險等級 參考價格 更多信息
KSZ8895RQXIA 1 Microchip Technology Inc DATACOM, ETHERNET TRANSCEIVER, PQFP128

ECAD模型

下載ECAD模型
$7.22 查看
TF202P32K7680R 1 CTS Corporation Parallel - Fundamental Quartz Crystal, 0.032768MHz Nom,
$3.82 查看
XUL516156.250000I 1 Integrated Device Technology Inc LVDS Output Clock Oscillator
$52.37 查看

相關(guān)推薦

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

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