【C++设计模式】详解装饰模式

2023年8月31日,周四上午

这是我目前碰到的最难的设计模式.....

非常难以理解而且比较灵活多半,学得贼难受,写得贼费劲.....

2023年8月31日,周四晚上19:48

终于写完了,花了一天的时间来学习装饰模式和写这篇博客。

虽然基本上把我今天的收获都写下来了,

但感觉写得还是不够好,有很多东西没有表达出来、表达清楚,

以后有空再更新吧....


目录

  • 概述
  • 装饰模式的含义
  • 使用装饰模式的好处
  • 为什么需要装饰模式
  • 什么时候使用装饰模式
  • 情景
  • 标准的装饰模式
  • 装饰模式的主要特征
  • 标准装饰模式程序示例
  • 我对标准装饰模式的思考
  • 使用标准装饰模式来写的示例程序

概述

装饰模式的含义

装饰模式是一种结构型设计模式,它允许向一个现有的对象添加新的功能,同时又不改变其结构。

使用装饰模式的好处

  • 可以动态地给对象添加责任,扩展对象功能。
  • 相比继承,装饰模式使用对象组合而不是继承来扩展功能。更加灵活。
  • 装饰模式支持开闭原则,扩展对象不修改其代码。

为什么需要装饰模式

  1. 动态扩展功能:装饰模式允许在运行时动态地给对象添加新的功能,而不需要修改原始对象的代码。这样可以避免类的继承层次的爆炸性增长,同时也更加灵活和可扩展。
  2. 单一职责原则:装饰模式通过将功能分散到多个装饰类中,每个装饰类只关注特定的功能,遵循了单一职责原则,使得代码更加清晰、可维护。
  3. 开放-封闭原则:装饰模式支持对现有代码的扩展,而不需要修改已有的代码,符合开放-封闭原则。这样可以避免因为修改现有代码而引入潜在的风险和错误。

什么时候使用装饰模式

  1. 需要在不改变现有对象结构的情况下,给对象增加新的功能或责任。
  2. 需要动态地给对象添加功能,并且这些功能可以组合和排列。
  3. 需要扩展一个类的功能,但是使用继承会导致类的继承层次过于庞大或不可行。
  4. 需要在运行时动态地给对象添加功能,而不是在编译时静态地确定功能。

情景

假设我有一个类Xxx,需要给它添加加法功能和减法功能,

但是由于这个类已经很复杂了、而且继承的层次已经很深了,

我不想改动里面的代码,也不想再多弄一个子类,那么怎么添加这两个功能呢?

class Xxx:public Ppp{
public:void func1();void func2();void func3();void func4();void func5();void func6();void func7();
};

在这种情况下,可以使用装饰模式来添加这两个功能,

因为这样既不用改动类内部代码,也不用再多弄一个子类。

// 原始类
class Xxx :public Ppp{
public:void func1() { /* 原始功能1的实现 */ }void func2() { /* 原始功能2的实现 */ }// ... 其他原始功能的实现void func7() { /* 原始功能7的实现 */ }
};// 装饰类 - 加法功能
class AddDecorator : public Xxx {
private:Xxx* component;public:AddDecorator(Xxx* component) : component(component) {}void func1() override {component->func1();// 加法功能的实现}
};// 装饰类 - 减法功能
class SubtractDecorator : public Xxx {
private:Xxx* component;public:SubtractDecorator(Xxx* component) : component(component) {}void func2() override {component->func2();// 减法功能的实现}
};int main() {Xxx* xxx = new Xxx();// 使用加法功能的装饰类Xxx* xxxWithAddition = new AddDecorator(xxx);xxxWithAddition->func1();// 使用减法功能的装饰类Xxx* xxxWithSubtraction = new SubtractDecorator(xxx);xxxWithSubtraction->func2();delete xxxWithSubtraction;delete xxxWithAddition;delete xxx;return 0;
}

注意,这并不是一个完整的装饰模式,但有助于理解装饰模式的作用。

标准的装饰模式

装饰模式的主要特征

  • 有一个抽象构件类,定义对象的接口。
  • 具体构件类实现抽象构件类。
  • 有一个装饰类继承抽象构件类,包含抽象构件类的对象引用。
  • 装饰类可以调用父类接口实现扩展功能。

标准装饰模式程序示例

#include <iostream>// 抽象构件类
class Component {
public:virtual void func() = 0;
};// 具体构件类
class ConcreteComponent : public Component {
public:void func() override {std::cout << "具体构件的操作" << std::endl;}
};// 装饰类
class Decorator : public Component {
protected:Component* component;public:Decorator(Component* component) : component(component) {}void func() override {if (component != nullptr) {component->func();}}
};// 具体装饰类A
class ConcreteDecoratorA : public Decorator {
public:ConcreteDecoratorA(Component* component) : Decorator(component) {}void addedBehavior() {std::cout << "具体装饰类的附加行为A" << std::endl;}void func() override {Decorator::func();addedBehavior();}
};// 具体装饰类B
class ConcreteDecoratorB : public Decorator {
public:ConcreteDecoratorB(Component* component) : Decorator(component) {}void addedBehavior() {std::cout << "具体装饰类的附加行为B" << std::endl;}void func() override {Decorator::func();addedBehavior();}
};int main() {Component* component = new ConcreteComponent();//用decoratedComponentA修饰componentComponent* decoratedComponentA = new ConcreteDecoratorA(component);//用decoratedComponentB修饰被decoratedComponentA修饰过的componentComponent* decoratedComponentB = new ConcreteDecoratorB(decoratedComponentA);decoratedComponentB->func();//要记得释放申请的堆内存delete decoratedComponentA;delete decoratedComponentB;delete component;return 0;
}

我对标准装饰模式的思考

装饰模式是怎么实现的不改动类原来的代码和结构就增加新的功能的?
装饰器是如何装饰具体构建类的?

他们有同一个抽象父类
这意味这什么?它们都会有抽象构件类的纯虚函数

装饰器需要引入抽象构件类
能引入抽象构件类,意味传入具体构件类对象后,只能调用其中抽象构件类有的方法
装饰器会重写父类的方法,并在重写的方法里面调用具体构件类的方法

具体装饰器有自己独特的成员和方法
具体装饰器会把具体构件类装入
具体装饰器会重写抽象装饰器的方法,并在重写的方法里面调用父类的方法和自己独特的方法
不管怎样,具体装饰类必须引入抽象构件类并传入具体构件类对象

装饰模式的核心在于重写?
重写的时候加入额外的方法

具体构件类需要实现抽象构件类的纯虚函数,否则无法被创建

装饰器的作用是什么?
引入具体构件类
在重写方法中调用具体构件类的方法

抽象构件类的作用是什么?

具体构件类的作用是什么?
定义最基础的功能

具体装饰器的作用是什么?
写额外的功能,然后通过重写方法加入额外的功能

使用标准装饰模式来写的示例程序

#include<iostream>class Product{
public:virtual void buy()=0;
};class Computer:public Product{
public:void buy ()override{std::cout<<"买了一台电脑"<<std::endl;}
};class Decorate:public Product{
private:Product *product;
public:Decorate(Product *product):product(product){};void buy() override{if(product!=nullptr){product->buy();}}
};class Mouse:public Decorate{
public:Mouse(Product *product):Decorate(product){};void buy() override{Decorate::buy();std::cout<<"又买了鼠标"<<std::endl;}
};class KeyBoard:public Decorate{
public:KeyBoard(Product *product):Decorate(product){};void buy() override{Decorate::buy();std::cout<<"又买了键盘"<<std::endl;}
};int main(){Product *computer=new Computer();Mouse *mouse=new Mouse(computer);KeyBoard *keyBoard=new KeyBoard(mouse);keyBoard->buy();
}

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

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

相关文章

序列到序列学习(seq2seq)

permute(1,0,2)&#xff0c;将batch_size 放在中间state 最后一个时刻&#xff0c;每个层的输出

数学建模--Subplot绘图的Python实现

目录 1.Subplot函数简介 2.Subplot绘图范例1:绘制规则子图 3.Subplot绘图范例2:绘制不规则子图 4.Subplot绘图范例3:gridspec辅助实战1 5.Subplot绘图范例4:gridspec辅助实战2 1.Subplot函数简介 """ 最近在数学建模种需要绘制多张子图,发现对于subplot函…

QT文件对话框,将标签内容保存至指定文件

一、主要步骤 首先&#xff0c;通过getSaveFileName过去想要保存的文件路径及文件名&#xff0c;其次&#xff0c;通过QFile类实例化一个文件对象&#xff0c;再读取文本框中的内容&#xff0c;最后将读取到的内容写入到文件中&#xff0c;最后关闭文件。 1.txt即为完成上述操作…

laragon 为 php 安装 Xdebug 扩展

众所周知&#xff0c;php 自带的 var_dump() 输出格式很不直观 而 laragon 作为很好的 windos 下开发环境很受欢迎&#xff0c;本文就介绍如何快速为 laragon 的 php 安装 Xdebug&#xff0c;方便开发调试 一&#xff1a;启动开发环境&#xff0c;在任意可访问 php 页面中输出 …

蓝桥杯备赛(Day5)——二叉树

二叉树存储 普通做法,二叉树一个节点包括结点的数值以及指向左右子节点的指针 在class Node中 def __init__(self,s,l=None,r=None):self.val=Noneself.l=lself.r=r 在竞赛中,我们往往使用静态数组实现二叉树,定义一个大小为N的静态结构体数组,使用其来存储一棵二叉树。…

python 学习笔记(4)—— webdriver 自动化操作浏览器(基础操作)

安装 web driver&#xff1a; 使用 driver 前&#xff0c;需要下载与浏览器版本相对应的 driver。如要在 Chrome 浏览器上操作&#xff0c;就要下载Chrome Driver。 几个常用浏览器的参考和下载地址&#xff1a; Edge Driver&#xff1a;https://developer.microsoft.com/en…

密码学入门——环游密码世界

文章目录 参考书目一、基本概念1.1 本书主要角色1.2 加密与解密 二、对称密码与公钥密码2.1 密钥2.2 对称密码和公钥密码2.3 混合密码技术 三、其他密码技术 参考书目 图解密码技术 第三版 一、基本概念 1.1 本书主要角色 1.2 加密与解密 加密 解密 密码破译 二、对称密…

Ansible之playbook详解和应用实例

目录 一、playbook简介 1.什么是playbook 2.playbook组成 二、应用实例 1.使用playbook安装启用httpd服务 2.使用playbook安装启用nginx服务 三、ansible-playbook其他用法 1.检查yaml文件的语法是否正确 2.检查tasks任务 3.检查指定的主机 4.指定从某个task开始运行…

Playwright for Python:断言

一、支持的断言 Playwright支持以下几种断言&#xff1a; 断言描述expect(locator).to_be_checked()复选框被选中expect(locator).to_be_disabled()元素是禁用状态expect(locator).to_be_editable()元素是可编辑状态expect(locator).to_be_empty()容器是空的expect(locator).…

Python列表排序

介绍一个关于列表排序的sort方法&#xff0c;看下面的案例&#xff1a; """ 列表的sort方法来对列表进行自定义排序 """# 准备列表 my_list [["a", 33], ["b", 55], ["c", 11]]# 排序&#xff0c;基于带名函数 …

Build阶段-Maven安装配置

构建Java项目的工具一般有两种选择&#xff0c;一个是Maven&#xff0c;一个是Gradle。 这里我们选择Maven作为项目的编译工具。 具体安装Maven流程不做阐述&#xff0c;但是需要确保配置好Maven仓库私服以及JDK编译版本

亚马逊云科技 云技能孵化营——我的云技能之旅

文章目录 每日一句正能量前言活动流程后记 每日一句正能量 不能在已经获得足够多的成功时&#xff0c;还对自己的能力保持怀疑&#xff0c;露出自信的微笑&#xff0c;走出自信的步伐&#xff0c;做一个自信的人&#xff01; 前言 亚马逊云科技 (Amazon Web Services) 是全球云…

镜像的基本命令(docker)

文章目录 前言一、docker命令介绍1、帮助命令2、显示镜像3、搜索镜像4、下载镜像5、删除镜像 总结 前言 本文主要介绍docker中与镜像相关的一些命令&#xff0c;是对狂神课程的一些总结&#xff0c;作为一个手册帮助博主和使用docker的同学们来查找和回忆。 实验环境&#xf…

【买华为云产品,返CSDN余额红包】,快来薅羊毛!

华为云828营销季火热进行中&#xff0c;9月15日前首次购买华为云产品官网任意一款产品&#xff0c;可获得相应比例的CSDN红包。 热门产品云服务器、域名、商标、主机安全等产品都在其中&#xff0c;任君挑选。 活动优惠价购买后还是获得相应比例余额红包&#xff0c;实际付费金…

【Redis专题】Redis持久化、主从与哨兵架构详解

目录 前言课程目录一、Redis持久化1.1 RDB快照&#xff08;Snapshot&#xff09;&#xff1a;二进制文件基本介绍开启/关闭方式触发方式bgsave的写时复制&#xff08;COW&#xff0c;Copy On Write&#xff09;机制优缺点 1.2 AOF&#xff08;append-only file&#xff09;&…

win11安装jdk

Windows11JDK20安装及环境变量配置 - 简书 Java学习--Win11配置环境变量-腾讯云开发者社区-腾讯云 电脑上安装多个JDK版本时如何自由切换_安装多版本jdk_有青枫林的博客-CSDN博客 Windows同时安装两个版本JDK&#xff0c;并实现动态切换JAVA8或者JAVA11 【无标题】windows1…

Gateway的服务网关

Gateway服务网关 Gateway网关是我们服务的守门神&#xff0c;所有微服务的统一入口。 网关的核心功能特性&#xff1a; 请求路由 权限控制 限流 架构如下&#xff1a; gateway使用 引入依赖 创建gateway服务&#xff0c;引入依赖 <!--网关--> <dependency>…

七、SSM 框架整合

目前已经学习了 MyBatis 框架&#xff0c;Spring 框架&#xff0c;以及Spring MVC 框架。现阶段学习将这三个框架整合到一起&#xff0c;实现简单的前后端交互的曾删改差功能页面。 Mybatis 框架主要负责数据库的操作问题&#xff0c;以及数据回显。该框架将 SQL 与 Jav…

JDK7多线程并发环境HashMap死循环infinite loop,CPU拉满100%,Java

JDK7多线程并发环境HashMap死循环infinite loop&#xff0c;CPU拉满100%&#xff0c;Java HashMap底层数据实现是数组链表&#xff0c;链表在哈希碰撞后装入新数据&#xff0c;像是一个桶。 HashMap在JDK7的实现中&#xff0c;并发环境存在死循环infinite loop问题。导致的结果…

异常-java

目录 一、异常的概念和体系结构 1.1 异常的概念 1.2 异常的体系结构 1.3 异常的分类 二、异常的处理 2.1 防御式编程 2.2 异常抛出 2.3 异常捕获 2.4 异常处理流程 三、自定义异常类 一、异常的概念和体系结构 1.1 异常的概念 程序员在开发过程中&#xff0c;想要将代码写得…