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

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

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

1 装饰器模式

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

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

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

2.1.2 主函数

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

#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接口实现,也是打印出服饰的名字。

// 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的展示接口:

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类与服饰类的代码如下:

// 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对象,

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

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

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++编程,来演示装饰模式的使用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/718457.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C#知识|模块化分层学习笔记

哈喽&#xff0c;你好&#xff0c;我是雷工&#xff01; 01 基本分层 典型的两层结构&#xff1a;由UI层 数据访问层 实体类构成。 其中实体类不算一层&#xff0c;本质是一个数据载体。 02 模块化分层 模块概念&#xff1a;在.NET平台中&#xff0c;模块主要是指类库项目。…

2024.6.17 作业 xyt

今日作业&#xff1a; 升级优化自己应用程序的登录界面。 要求&#xff1a; 1. qss实现 2. 需要有图层的叠加 &#xff08;QFrame&#xff09; 3. 设置纯净窗口后&#xff0c;有关闭等窗口功能。 4. 如果账号密码正确…

Modbus协议转Profibus协议模块接热传感器配置攻略

一、前言 在工业自动化控制领域&#xff0c;Modbus协议和Profibus协议是两种常见的通讯协议&#xff0c;它们在设备之间传输数据起着至关重要的作用。而Modbus协议转Profibus协议模块&#xff08;XD-MDPB100&#xff09;设备&#xff0c;则扮演着连接不同通讯协议的桥梁角色。…

新质生产力水平测算与中国经济增长新动能(dta数据及do代码)

时间跨度&#xff1a;2012-2022年 数据范围&#xff1a;全国30个省份&#xff08;不含港澳台、西藏&#xff09; 数据指标&#xff1a; 参考韩文龙等的做法&#xff0c;收集了全部控制变量与稳定性检验所需变量。 类型 符号 变量 变量定义 被解释变量 GDP 各省人均GDP…

Linux 并发与竞争基础知识学习

Linux 并发与竞争 并发与竞争 Linux 系统是个多任务操作系统&#xff0c;会存在多个任务同时访问同一片内存区域&#xff0c;这些任务可能会相互覆盖这段内存中的数据&#xff0c;造成内存数据混乱。针对这个问题必须要做处理&#xff0c;严重的话可能会导致系统崩溃。现在的…

Swift开发——存储属性与计算属性

Swift语言开发者建议程序设计者多用结构体开发应用程序。在Swift语言中,结构体具有了很多类的特性(除类的与继承相关的特性外),具有属性和方法,且为值类型。所谓的属性是指结构体中的变量或常量,所谓的方法是指结构体中的函数。在结构体中使用属性和方法是因为:①匹别于结…

[ARM-2D 专题]3. ##运算符

C语言的宏系统相当强大&#xff0c;它允许使用##符号来处理预处理期的文本替换。这种用法被称为标记连接&#xff08;token pasting&#xff09;操作&#xff0c;其结果是将两个标记紧紧地连接在一起&#xff0c;而省略掉它们之间的所有空格。在复杂的宏定义中&#xff0c;运用…

数组元素的内存地址计算【数据结构与算法C#版】

数组元素被存储在连续的内存空间中&#xff0c;这意味着计算数组元素的内存地址非常容易。给定数组内存地址&#xff08;首 元素内存地址&#xff09;和某个元素的索引&#xff0c;我们可以使用下方图 所示的公式计算得到该元素的内存地址&#xff0c;从而直接 访问该元素。 观…

探索CSS clip-path: polygon():塑造元素的无限可能

在CSS的世界里&#xff0c;clip-path 属性赋予了开发者前所未有的能力&#xff0c;让他们能够以非传统的方式裁剪页面元素&#xff0c;创造出独特的视觉效果。其中&#xff0c;polygon() 函数尤其强大&#xff0c;它允许你使用多边形来定义裁剪区域的形状&#xff0c;从而实现各…

【C语言】排序算法 -------- 计数排序

个人主页 创作不易&#xff0c;感谢大家的关注&#xff01; 文章目录 1. 计数排序的概念2. 计数排序使用场景3. 计数排序思想4. 计数排序实现过程5. 计数排序的效率6. 总结&#xff08;附源代码&#xff09; 1. 计数排序的概念 计数排序是一种非比较的排序算法&#xff0c;其…

二、交换机介绍及vlan原理

目录 一、交换机 1.1、交换机处理数据帧的三种行为 1.2、初始化通信 二、虚拟局域网&#xff08;VLAN&#xff09; 三、vlan间通信 3.1、子接口 3.2、三层交换机 一、交换机 交换机&#xff1a;隔离冲突域&#xff0c;交换机每个接口都有一个网卡&#…

解放代码:识别与消除循环依赖的实战指南

目录 一、对循环依赖的基本认识 &#xff08;一&#xff09;代码中形成循环依赖的说明 &#xff08;二&#xff09;无环依赖的原则 二、识别和消除循环依赖的方法 &#xff08;一&#xff09;使用JDepend识别循环依赖 使用 Maven 集成 JDepend 分析报告识别循环依赖 &a…

超越中心化:Web3如何塑造未来数字生态

随着技术的不断发展&#xff0c;人们对于网络和数字生态的期望也在不断提升。传统的中心化互联网模式虽然带来了便利&#xff0c;但也暴露出了诸多问题&#xff0c;比如数据滥用、信息泄露、权力集中等。在这样的背景下&#xff0c;Web3技术应运而生&#xff0c;旨在打破传统中…

Shopee API接口:获取搜索栏生成的商品结果列表

一、平台介绍 Shopee&#xff0c;作为东南亚领先的电商平台&#xff0c;一直致力于为卖家和买家提供便捷、高效的在线购物体验。为了满足广大开发者的需求&#xff0c;Shopee提供了丰富的API接口服务&#xff0c;帮助卖家和第三方开发者更好地与平台进行数据交互&#xff0c;实…

ucos抢占式实时多任务操作系统 (RTOS)。

介绍 uCOS (也称为 μC/OS 或 Micro-Controller Operating System) 是一个开源的、可移植的、可裁剪的、抢占式实时多任务操作系统 (RTOS)。它最初由 Jean J. Labrosse 编写&#xff0c;并广泛用于嵌入式系统设计中。uCOS 是一个小型的 RTOS&#xff0c;非常适合那些需要实时性…

区间预测 | Matlab实现CNN-ABKDE卷积神经网络自适应带宽核密度估计多变量回归区间预测

区间预测 | Matlab实现CNN-ABKDE卷积神经网络自适应带宽核密度估计多变量回归区间预测 目录 区间预测 | Matlab实现CNN-ABKDE卷积神经网络自适应带宽核密度估计多变量回归区间预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现CNN-ABKDE卷积神经网络自适应…

基于深度学习网络的USB摄像头实时视频采集与人脸检测matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 将摄像头对这播放视频的显示器&#xff0c;然后进行识别&#xff0c;识别结果如下&#xff1a; 本课题中&#xff0c;使用的USB摄像头为&#xff…

30.保存游戏配置到文件

上一个内容&#xff1a;29.添加录入注入信息界面 以 29.添加录入注入信息界面 它的代码为基础进行修改 效果图&#xff1a; 首先在我们辅助程序所在目录下创建一个ini文件 文件内容 然后首先编写一个获取辅助程序路径的代码 TCHAR FileModule[0x100]{};GetModuleFileName(NUL…

【教学类-12-12】20240617通义万相-动物图片6张编故事(A4一页4条)

背景需求 【教学类-12-11】20240612通义万相-动物图片连连看&#xff08;A4一页3套&#xff09;-CSDN博客文章浏览阅读891次&#xff0c;点赞34次&#xff0c;收藏11次。【教学类-12-11】20240612通义万相-动物图片连连看&#xff08;A4一页3套&#xff09;https://blog.csdn.n…

Web前端项目-拼图游戏【附源码】

拼图游戏 拼图游戏是一种经典的益智游戏&#xff0c;通过HTML、CSS和JavaScript等前端技术的综合运用来实现&#xff1b;拼图游戏可以锻炼玩家的观察能力、空间认知能力和逻辑思维能力。游戏开始时&#xff0c;一张图片会被切割成多个小块&#xff0c;并以随机顺序排列在游戏区…