• 正文
  • 推薦器件
  • 相關(guān)推薦
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

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

2024/06/24
879
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(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章:依賴(lài)倒轉(zhuǎn)原則和里氏替換原則),這些原則在設(shè)計(jì)模式中用到時(shí)會(huì)提及,暫不做專(zhuān)門(mén)解讀。

1 裝飾器模式

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

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

2 穿搭衣服實(shí)例

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

2.1 版本一

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

2.1.1 Person類(lèi)

Person類(lèi)的代碼如下,維護(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類(lèi)了,這違反了面向?qū)ο笤O(shè)計(jì)中的開(kāi)放-封閉原則。

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

換句話說(shuō):

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

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

2.2 版本二

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

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

2.2.1 Person類(lèi)與服飾類(lèi)

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

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

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

// 各種服飾子類(lèi)
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í)例化具體要裝扮的服飾類(lèi)并調(diào)用對(duì)應(yīng)的展示接口,

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

int main()
{
    Person xc = Person("小菜"); //先實(shí)例化一個(gè)名為"小菜"的Person對(duì)象
    
    printf("n第一種裝扮:");
    TShirts dtx; //依次實(shí)例化具體要裝扮的服飾類(lèi)
    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類(lèi)和服飾類(lèi)是完全獨(dú)立的,搭配衣服的過(guò)程也只是一個(gè)一個(gè)將對(duì)應(yīng)詞打印出來(lái)。下面來(lái)看版本三。

2.3 版本三

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

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

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

2.3.1 Person類(lèi)與服飾類(lèi)

Person類(lèi)與服飾類(lèi)的代碼如下:

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

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

// 具體服飾類(lèi)(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í)例化要裝扮的服飾類(lèi)對(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à)格 更多信息
ABM11AIG-40.000MHZ-4Z-T3 1 Abracon Corporation CRYSTAL 40MHZ 10PF SMD
$1.44 查看
AFBR-720XPDZ 1 Foxconn Transceiver, 840nm Min, 860nm Max, 10000Mbps(Tx), 10000Mbps(Rx), LC Connector, Board/panel Mount, ROHS COMPLIANT PACKAGE-30
$164.34 查看
9DBV0631BKILFT 1 Integrated Device Technology Inc VFQFPN-40, Reel

ECAD模型

下載ECAD模型
暫無(wú)數(shù)據(jù) 查看

相關(guān)推薦

登錄即可解鎖
  • 海量技術(shù)文章
  • 設(shè)計(jì)資源下載
  • 產(chǎn)業(yè)鏈客戶資源
  • 寫(xiě)文章/發(fā)需求
立即登錄