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

news2024/11/26 22:16:53

本篇文章,来解读《大话设计模式》的第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++编程,来演示装饰模式的使用。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

吴恩达2022机器学习专项课程C2W3:2.26 机器学习发展历程

目录 开发机器学习系统的过程开发机器学习案例1.问题描述2.创建监督学习算法3.解决问题4.小结 误差分析1.概述2.误差分析解决之前的问题3.小结 增加数据1.简述2.增加数据案例一3.增加数据案例二4.添加数据的技巧5.空白创建数据6.小结 迁移学习1.简述2.为什么迁移学习有作用3.小…

考勤机如何选购

考勤机如何选购 选购目的&#xff1a;为强化管理&#xff0c;保证公司考勤工作顺利开展&#xff0c;拟在我司采用指纹识别技术进行员工考勤&#xff0c;如此可有效提升工作效率&#xff0c;而指纹识别考勤技术又是最为便捷、可靠且非侵入性的考勤方式。 指纹考勤…

Python Django 实现教师、学生双端登录管理系统

文章目录 Python Django 实现教师、学生双端登录管理系统引言Django框架简介环境准备模型设计用户认证视图和模板URL路由前端设计测试和部署获取开源项目参考 Python Django 实现教师、学生双端登录管理系统 引言 在当今的教育环境中&#xff0c;数字化管理系统已成为必不可少…

Hi3861 OpenHarmony嵌入式应用入门--点灯

本篇实现对gpio的控制&#xff0c;通过控制输出进行gpio的点灯操作。 硬件 我们来操作IO2&#xff0c;控制绿色的灯。 软件 GPIO API API名称 说明 hi_u32 hi_gpio_deinit(hi_void); GPIO模块初始化 hi_u32 hi_io_set_pull(hi_io_name id, hi_io_pull val); 设置某个IO…

一文教你使用sql快速查询1对多数据关系,生成一个多维数据!

本篇文章主要讲解&#xff1a;利用mysql的sql特性&#xff0c;实现对多表查询下&#xff0c;一个用户对应多条记录数据查询为一条数据并以列表形式显示的教程。 日期&#xff1a;2024年6月17日 作者&#xff1a;任聪聪 一、创建数据库表 创建test_a表&#xff0c;内容如下&…

Python 全栈系列250 数据流实践

说明 之前好几个企业都提过飞机在飞的时候换发动机的例子&#xff0c;来比喻变革是多么无奈和危险。还有的说法更直接&#xff1a;不变等死&#xff0c;变就是找死&#xff0c;总得选一样。 后来我自己的体会也差不多是这样&#xff0c;总有一些窘境让你抉择&#xff1a;是忽忽…

在Qt编写的exe或者dll中设置版本号

1.背景 在别人编写的exe或者动态库中&#xff0c;通过右键–》属性–》详细信息中&#xff0c;通常都有版本信息&#xff1a; 那我们自己编译出来的Qt程序&#xff0c;如何设置这些版本号呢&#xff1f; 2.解决方案 参考【.pro文件中设置版本等信息】&#xff0c;只要在工…

【完结】无代码网页爬虫软件——八爪鱼采集器入门基础教程

《八爪鱼采集器入门基础教程》大纲如下&#xff1a; 课程所提软件&#xff0c;八爪鱼采集器下载&#xff1a; 1.软件分享[耶]八爪鱼&#xff0c;爬取了几百条网站上的公开数据&#xff0c;不用学代码真的很方便。[得意]2.发现了一个很棒的软件&#xff0c;?不用学python也可…

【UEditorPlus】后端配置项没有正常加载,上传插件不能正常使用

接上文【UEditorPlus】后端配置项没有正常加载&#xff0c;上传插件不能正常使用_ueditor ruoyi vue后端配置项没有正常加载,上传插件不能正常使用!-CSDN博客 成功解决图片、视频上传问题后&#xff0c;当服务部署在公网时&#xff0c;会存在大文件无法正常上传的问题。 出现…

微软修复Wi-Fi驱动高危漏洞 影响Windows所有版本 可通过局域网发起攻击

微软修复 WiFi 驱动程序中的高危安全漏洞&#xff0c;该漏洞影响 Windows 所有版本&#xff0c;可以通过公共 WiFi 等发起攻击。如果攻击者能够接入到目标设备所处的网络中&#xff0c;则可以悄无声息完成攻击&#xff0c;不需要用户进行任何交互。 在本月例行安全更新中微软修…

比特币的共识协议

目录 前言 一、铸币交易 1.铸币权 2.铸币交易&#xff08;coinbase tx&#xff09; 3.输入和输出 二、小型的区块链 1.见下图所示 2.哈希指针 3.双花攻击 三、块头块身&#xff0c;全节点轻节点 1.块头(block head) 2.块身 3.全节点full node(fully validating node…

操作系统入门系列-MIT6.828(操作系统工程)学习笔记(六)---- 初窥操作系统启动流程(xv6启动)

系列文章目录 操作系统入门系列-MIT6.S081&#xff08;操作系统&#xff09;学习笔记&#xff08;一&#xff09;---- 操作系统介绍与接口示例 操作系统入门系列-MIT6.828&#xff08;操作系统工程&#xff09;学习笔记&#xff08;二&#xff09;----课程实验环境搭建&#x…

数据库系统概论(个人笔记)(第四部分)

数据库系统概论&#xff08;个人笔记&#xff09; 文章目录 数据库系统概论&#xff08;个人笔记&#xff09;4、中间的SQL4.1 连接表达式4.2 视图4.3 事务4.4 完整性约束4.5 SQL数据类型和模式4.6 SQL中的索引定义4.7 授权 4、中间的SQL 4.1 连接表达式 Join Expressions Join…

10.无代码爬虫软件做网页数据抓取流程——工作流程设置与数据预览

首先&#xff0c;多数情况下免费版本的功能&#xff0c;已经可以满足绝大多数采集需求&#xff0c;想了解八爪鱼采集器版本区别的详情&#xff0c;请访问这篇帖子&#xff1a;https://blog.csdn.net/cctv1123/article/details/139581468 八爪鱼采集器免费版和个人版、团队版下…

视频监控平台:通过网络SDK对TCL网络摄像机进行PTZ控制 的源代码介绍及分享

目录 一、视频监控平台介绍 &#xff08;一&#xff09;概述 &#xff08;二&#xff09;视频接入能力介绍 &#xff08;三&#xff09;功能介绍 二、TCL网络摄像机 &#xff08;一&#xff09;360度全景自动旋转&#xff1a; &#xff08;二&#xff09;高清夜视和全彩…

五、路由协议

目录 一、为何划分子网&#xff1f; 二、静态路由 三、OSPF 3.1、OSPF原理 3.1.1、ospf四步过程 3.2、OSPF区域划分 3.2.1、为什么划区域&#xff1f; 3.2.2、划分规则 3.3、OSPF参数 3.4、五种报文 3.4.1、hello报文 3.4.2、DD报文 3.4.2、LSR、LSU、LSAck报文 3…

黄仁勋:下一波AI的浪潮是物理AI

B站&#xff1a;啥都会一点的研究生公众号&#xff1a;啥都会一点的研究生 最近AI圈又发生了啥&#xff1f; 快手视频生成大模型“可灵”开放邀测&#xff0c;效果对标 Sora 在OpenAl文生视频大模型Sora发布后&#xff0c;国内企业争相入局&#xff0c;快手视频生成大模型可…

MySQL之优化服务器设置和复制(一)

优化服务器设置 操作系统状态 CPU密集型的机器 CPU密集型服务器的vmstat输出通常在us列会有一个很高的值&#xff0c;报告了花费在非内核代码上的CPU时钟;也可能在sy列有很高的值&#xff0c;表示系统CPU利用率&#xff0c;超过20%就足以令人不安了。在大部分情况下&#xff…

pyqt 鼠绘椭圆 椭圆标注

目录 pyqt 椭圆标注 四个方向可以调整,调整时,另一端固定,只调整当前端,椭圆参数保存加载json pyqt 画椭圆中心点固定,调整是,两端一起调整。 pyqt 椭圆标注 四个方向可以调整,调整时,另一端固定,只调整当前端,椭圆参数保存加载json import sys import json from …

React@16.x(32)useDebugValue

目录 1&#xff0c;介绍2&#xff0c;作用 1&#xff0c;介绍 从一个例子开始&#xff1a; export default function App() {const [n, setN] useState(0);const refH1 useRef();useEffect(() > {console.log("父组件");});return <h1 ref{refH1}>{n}&l…