前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >《大话设计模式》解读03-装饰模式

《大话设计模式》解读03-装饰模式

作者头像
xxpcb
发布2024-06-17 12:53:10
1680
发布2024-06-17 12:53:10
举报
文章被收录于专栏:码农爱学习的专栏

本篇文章,来解读《大话设计模式》的第6章——装饰模式。并通过C++代码实现实例代码的功能。

关注“码农爱学习”,设置“星标公众号

注:第3~5章讲的是设计模式中的一些原则(第3章:单一职责原则;第4章:开放-封闭原则;第5章:依赖倒转原则和里氏替换原则),这些原则在设计模式中用到时会提及,暂不做专门解读。

1 装饰器模式

装饰模式,或称装饰器模式(Decorator),动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更加灵活

我们在给对象增加功能时,一种做法是再设计一个子类来继续父类,然后给子类添加额外的功能,另一种做法就是通过装饰模式,设计单独用于装饰功能的类,通过“包装”的形式动态给某个对象增加功能。

2 穿搭衣服实例

题目:用控制台程序,写可以给人搭配衣服的代码

2.1 版本一

版本一的代码,仅定义了一个Person类,提供6种不同服饰的装扮接口,以及1个名字展示接口。

2.1.1 Person类

Person类的代码如下,维护一个人的名字,然后是6种服饰的穿衣接口,就是加一句打印,最后Show接口显示人的名字。

代码语言:javascript
复制
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("领带 ");
    }
    
    void WearLeatherShoes()
    {
        printf("皮鞋 ");
    }
    
    void Show()
    {
        printf("装扮的%s", m_name.c_str());
    }  
             
private:
    std::string m_name;
};

2.1.2 主函数

主函数的逻辑如下,先实例化一个名为"小菜"的Person对象,然后依次调用穿衣接口,最后调用展示接口:

代码语言:javascript
复制
#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;
}

代码运行效果如下:

版本一这种方式,虽然功能实现了,但如果想要增加装扮,就需要修改Person类了,这违反了面向对象设计中的开放-封闭原则。

开放-封闭原则:是指软件实体(类、模块、函数等等)应该可以扩展,但是不可修改。 换句话说:

  • 开放:对扩展开放,当需要增加新功能时,通过代码扩展的方式(如增加新的类)实现
  • 封闭:对修改封闭,当需要增加新功能时,尽量避免对原有代码的修改

下面来看版本二如何实现。

2.2 版本二

版本二是将各种服饰单独封装了起来,并继承自服饰抽象类,将服饰类与人类进行了分离,类图如下:

这样,后续需要增加服饰时,只需要增加对应的具体服饰类,而不会影响其它已有的服饰类的代码。

2.2.1 Person类与服饰类

Person类与服饰类的代码如下,Person类只维护一个人的名字,并通过Show接口显示人的名字。服饰类的Show接口用于显示服饰的名称,具体显示的内容由具体服饰类的Show接口实现,也是打印出服饰的名字。

代码语言:javascript
复制
// 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("领带 ");
    }    
};

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

2.2.2 主函数

主函数的逻辑如下,先实例化一个名为"小菜"的Person对象,

然后依次实例化具体要装扮的服饰类并调用对应的展示接口,

最后调用Person的展示接口:

代码语言:javascript
复制
int main()
{
    Person xc = Person("小菜"); //先实例化一个名为"小菜"的Person对象
    
    printf("\n第一种装扮:");
    TShirts dtx; //依次实例化具体要装扮的服饰类
    BigTrouser kk;
    Sneakrs pqx;
    dtx.Show(); //调用对应的展示接口
    kk.Show();
    pqx.Show();
    xc.Show(); //最后调用Person的展示接口
    
    printf("\n第二种装扮:");
    Suit xz;
    Tie ld;
    LeatherShoes px;
    xz.Show();
    ld.Show();
    px.Show();
    xc.Show(); 
    
    printf("\n");  
    
    return 0;
}

代码运行效果如下:

版本二中,Rerson类和服饰类是完全独立的,搭配衣服的过程也只是一个一个将对应词打印出来。下面来看版本三。

2.3 版本三

版本三用到了本篇要讲的装饰器模式。

在本例中,服饰就是装饰类,具体的服饰,T恤、球鞋这些是具体的装饰类,而人就是要被装饰的组件,那是组件Component还是具体组件ConcreateComponet呢?

本例中,只有一个ConcreateComponet具体组件类,就没必要单独建立一个Component组件类,可以把两者职责合并成一个类,也就是只使用ConcreateComponet类表示Person类。

2.3.1 Person类与服饰类

Person类与服饰类的代码如下:

代码语言:javascript
复制
// Person类
class Person
{
public:
    Person(){};
    
    Person(std::string name)
    {
        m_name = name;
    }
    
    // 父类的函数
    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;
    }
    
    // 子类的函数
    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("领带 ");
        Finery::Show();
    }    
};

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

2.3.2 主函数

主函数的逻辑如下,先实例化一个名为"小菜"的Person对象,

然后再依次实例化要装扮的服饰类对象,接着通过“包装”的方式,后一个对象对前一个对象进行包装,

最后调用最终包装后对象的展示接口:

代码语言:javascript
复制
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();       // 最后调用最外层的装饰对象的展示接口

    printf("\n第二种装扮:");
    Suit         *xz = new Suit();
    Tie          *ld = new Tie();
    LeatherShoes *px = new LeatherShoes();
    
    xz->Decorate(xc); // 装饰过程:先用西装装饰小菜
    ld->Decorate(xz); // 再用领带装饰穿了西装的小菜
    px->Decorate(ld); // 再皮鞋装饰穿了西装和领带的小菜
    px->Show();       // 最后调用最外层的装饰对象的展示接口  

    printf("\n");  
    
    return 0;
}

代码运行效果如下:

装饰模式的优缺点与适用场景:

3 总结

本篇介绍了设计模式中的装饰模式,并通过给人装扮的实例,使用C++编程,来演示装饰模式的使用。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-06-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码农爱学习 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1 装饰器模式
  • 2 穿搭衣服实例
    • 2.1 版本一
      • 2.1.1 Person类
      • 2.1.2 主函数
    • 2.2 版本二
      • 2.2.1 Person类与服饰类
      • 2.2.2 主函数
    • 2.3 版本三
      • 2.3.1 Person类与服饰类
      • 2.3.2 主函数
  • 3 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档