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

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

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

相关文章

什么是扩展运算符;

在JavaScript中&#xff0c;扩展运算符&#xff08;Spread Operator&#xff09;是另一种常用的运算符&#xff0c;尤其在处理数组和对象时非常有用。它以三个连续的点&#xff08;…&#xff09;表示&#xff0c;可以将数组、对象或字符串展开为它们的个别元素或属性。这种运算…

嵌入式linux系统中SPI子系统原理分析01

大家好,今天给大家分享一下,如何使用linux系统中的SPI通信协议,实现主从设备之间的信息传递。 SPI是一种常见的设备通用通信协议。它是一个独特优势就是可以无中断发送数据,可以连续发送或接收任意数量的位。而在I2C和UART中,数据以数据包的形式发送,有限定位数。 …

FreeBSD通过CBSD管理低资源容器jail来安装Ubuntu子系统实践

简介 FreeBSD、CBSD、Jail和Ubuntu&#xff0c;四者的组合方案可以说是强强联合&#xff0c;极具性价比和竞争力&#xff01;同时安装简单方便&#xff0c;整体方案非常先进。 CBSD是为FreeBSD jail子系统、bhyve、QEMU/NVMM和Xen编写的管理层。该项目定位为一个综合解决方案…

【操作系统】线程进程相关

线程 & 进程 1 进程 2 线程 2.1 线程保序 在C中&#xff0c;线程的执行顺序是由操作系统调度的&#xff0c;并且通常是不能被程序员直接控制的。也就是说&#xff0c;你不能直接强制线程按照特定的顺序执行。然而&#xff0c;你可以使用某些同步机制来影响线程的执行顺…

MinIO:构建未来的开源对象存储解决方案

MinIO&#xff1a;构建未来的开源对象存储解决方案 在当今数据驱动的世界中&#xff0c;对象存储已成为云计算环境中不可或缺的一部分。随着数据量的激增&#xff0c;企业和开发者们都在寻找既高效又经济的数据存储方案。在这个背景下&#xff0c;MinIO应运而生&#xff0c;它…

Win和Linux的状态空间模型安装教程

目录 一、Linux系统安装 二、Win系统安装 1&#xff09;、安装causal_conv1d 1、第一种方法 2、第二种方法&#xff08;感觉可靠&#xff09; 3、第三种方法&#xff1a;直接下载大神编译好的文件进行安装 2&#xff09;、安装mamba-ssm 1、第一种方法 2、第二种方法&…

wondershaper 一款限制 linux 服务器网卡级别的带宽工具

文章目录 一、关于奇迹整形器二、文档链接三、源码下载四、限流测试五、常见报错1. /usr/local/sbin/wondershaper: line 145: tc: command not found2. Failed to download metadata for repo ‘appstream‘: Cannot prepare internal mirrorlist: No URLs.. 一、关于奇迹整形…

Memcached:高性能分布式缓存系统的奥秘解锁

在高并发的互联网应用场景中&#xff0c;缓存系统的作用至关重要。它可以显著提高系统的响应速度&#xff0c;减轻数据库的压力。在众多缓存技术中&#xff0c;Memcached以其简单、高效、稳定的特性脱颖而出&#xff0c;成为了全球众多互联网公司的选择。本文将为您全面揭秘Mem…

Langchain中使用Ollama提供的Qwen大模型进行Function Call实现天气查询、网络搜索

Function Call&#xff0c;或者叫函数调用、工具调用&#xff0c;是大语言模型中比较重要的一项能力&#xff0c;对于扩展大语言模型的能力&#xff0c;或者构建AI Agent&#xff0c;至关重要。 Function Call的简单原理如下&#xff1a; 按照特定规范&#xff08;这个一般是L…

vue跨域问题,请注意你的项目是vue2还是vue3

uniapp跨域设置了&#xff0c;但还是有问题 uniapp设置代理后还是无法请求后端接口vue2项目设置代理vue3项目设置代理 uniapp设置代理后还是无法请求后端接口 如果你在possman&#xff0c;apifox上测试接口都没有问题&#xff0c;但是在hbuild项目中设置代理后&#xff0c;还是…

多尺度特征提取:原理、应用与挑战

多尺度 多尺度特征提取&#xff1a;原理、应用与挑战**原理****应用****挑战****总结** 多尺度特征提取&#xff1a;原理、应用与挑战 在计算机视觉、自然语言处理和信号处理等领域&#xff0c;有效地捕捉和解析数据的多种尺度特性是至关重要的。多尺度特征提取是一种技术&…

数据治理:让数据提取更高效、更准确的关键

数据治理&#xff1a;让数据提取更高效、更准确的关键 在数字化浪潮的推动下&#xff0c;数据已成为企业运营和决策的重要基石。然而&#xff0c;单纯的数据堆积并不能带来实际的业务价值&#xff0c;关键在于如何高效、准确地提取并利用这些数据。而数据治理&#xff0c;作为…

融资融券有哪些交易技巧,两融利率现在最低多少?4.0%!

融资融券交易技巧 授信额度技巧 当我们账户净资产有显著增长时&#xff0c;最好主动申请增加信用额度&#xff0c;这样在后面行情好转入资金需要进行更多融资融券交易时就不会受限于授信额度&#xff0c;避免因为临时申请增加额度而错过交易机会。 买入委托技巧 现金的折算率…

小孟再接盲盒小程序,3天开发完!

大家好&#xff0c;我是程序员小孟。 前面开发了很多的商业的单子&#xff0c;私活联盟的小伙伴慢慢的逐渐搞自己的产品。 前面的话&#xff0c;开发了盲盒小程序&#xff0c;最近又接了一款盲盒小程序。因为前面有开发过&#xff0c;所以我们的成本也少了很多。 盲盒小程序…

一个基于MySQL的数据库课程设计的基本框架

数据库课程设计&#xff08;MySQL&#xff09;通常涉及多个步骤&#xff0c;以确保数据库的有效设计、实现和维护。以下是一个基于MySQL的数据库课程设计的基本框架&#xff0c;结合参考文章中的相关信息进行整理&#xff1a; ### 一、引言 * **背景**&#xff1a;简要介绍为…

走进Elasticsearch

什么是ES 是一个分布式、RESTful风格的搜索和数据分析引擎 中文参考文档&#xff1a; 《Elasticsearch中文文档》 | Elasticsearch 技术论坛 elasticSearch官网&#xff1a; Functions and Operators | Elasticsearch Guide [7.11] | Elastic查询方式 Kibana查询&#xff08;原…

2024.6.17 作业 xyt

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

代码随想录算法训练营刷题复习4 :单调栈

单调栈 单调栈 如果题目出现典型的 【左小 中大(栈中左侧元素都比此值小) || 右小】&#xff08;寻找右侧第一个比此值小的元素&#xff09; 【左大 中小(栈中左侧元素都比此值大) || 右大】&#xff08;寻找右侧第一个比此值大的元素&#xff09; 数据关系的话&#xff0c;可…

Marin说PCB之orcad-capture原理图封装库的创建总结----01

今天是个不错的日子&#xff0c;我早上一出门刚骑车到半路就开始下大雨了&#xff0c;可是天气预报上明明说的没有雨啊&#xff0c;所以说天气预报就像是女人的脾气一样&#xff0c;难以揣摩啊&#xff0c;也尽量少去揣摩吧。 小编我刚刚到公司&#xff0c;就收到美国分部同事J…

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

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