设计模式之装饰器模式
面向对象设计原则
接口隔离原则:面向对象设计之接口隔离原则-CSDN博客
设计模式
工厂模式 : 设计模式之工厂模式-CSDN博客
迭代器模式:设计模式之迭代器模式-CSDN博客
适配器模式:设计模式之适配器模式-CSDN博客
过滤器模式:设计模式之过滤器模式-CSDN博客
单例模式:设计模式之单例模式-CSDN博客
观察者模式:设计模式之观察者模式-CSDN博客
空对象模式:设计模式之空对象模式-CSDN博客
目录
1.概述
2.结构
3.实现
3.1.示例1:简单实现
3.2.示例2:函数装饰器
4.总结
1.概述
装饰器模式也是我们日常编程用的比较多的一种设计模式,它允许向一个现有的对象添加新的功能,同时又不改变其结构,实际上就是现有的类的一个包装。这种模式可以视为结构性模式,应为它提供了一种用于组合类的机制,,使得这些类可以在不改变其代码的情况下进行扩展。
在装饰器模式中,有一个被装饰的组件(通常是抽象类或接口),它定义了需要实现的方法。然后,有一个装饰器类,它实现了同样的接口,并且包装了被装饰的组件。装饰器类可以在被装饰的组件方式调用之前或之后添加额外的功能。
2.结构
装饰器模式的UML类图如下:

主要角色:
1)抽象组件(Component):定义了原始对象和装饰器对象的公共接口或抽象类,可以是具体组件类的父类或接口。
2)具体组件(Concrete Component):是被装饰的原始对象,它定义了需要添加新功能的对象。
3)抽象装饰器(Decorator):这是一个抽象基类或接口,它也实现了组件接口,并包含一个成员变量用来引用具体组件对象。装饰器可以添加额外的功能,通常通过在装饰器中委托给具体组件实现。
4)具体装饰器(Concrete Decorator):实现了抽象装饰器的接口,负责向抽象组件添加新的功能。具体装饰器通常会在调用原始对象的方法之前或之后执行自己的操作。
装饰器模式通过嵌套包装多个装饰器对象,可以实现多层次的功能增强。每个具体装饰器类都可以选择性地增加新的功能,同时保持对象接口的一致性。
在C++中,装饰器模式通常涉及到类的继承和组合,以及对虚函数的使用,以确保适当的方法可以在组合中被调用。这使得C++的装饰器模式非常灵活,可用于构建可维护和可扩展的对象结构。
3.实现
3.1.示例1:简单实现
Decorator.h
#include <iostream>
#include <memory>// 抽象组件
class Component {
public:virtual void doWork() = 0;
};// 具体组件
class ConcreteComponent : public Component {
public:void doWork() override {// 执行具体操作std::cout << "ConcreteComponent doWork..." << std::endl;}
};// 抽象装饰器
class Decorator : public Component {
protected:Component* m_pComponent;
public:Decorator(Component* pComponent) : m_pComponent(pComponent) {}void doWork() override {m_pComponent->doWork();}
};// 具体装饰器
class ConcreteDecorator1 : public Decorator {
public:ConcreteDecorator1(Component* pComponent) : Decorator(pComponent) {}void doWork() override {std::cout << "ConcreteDecorator1 doWork before..." << std::endl;__super::doWork();std::cout << "ConcreteDecorator1 doWork after..." << std::endl;}
};// 具体装饰器
class ConcreteDecorator2 : public Decorator {
public:ConcreteDecorator2(Component* pComponent) : Decorator(pComponent) {}void doWork() override {std::cout << "ConcreteDecorator2 doWork before..." << std::endl;__super::doWork();std::cout << "ConcreteDecorator2 doWork after..." << std::endl;}
}; 
main.cpp
int main()
{std::unique_ptr<Component> pConcreteComponent(new ConcreteComponent());std::unique_ptr<Component> pConcreteDecorator1(new                     ConcreteDecorator1(pConcreteComponent.get()));std::unique_ptr<Component> pConcreteDecorator2(new ConcreteDecorator2(pConcreteDecorator1.get()));pConcreteComponent->doWork();std::cout << std::endl;pConcreteDecorator1->doWork();std::cout << std::endl;pConcreteDecorator2->doWork();
} 
输出:
ConcreteComponent doWork...ConcreteDecorator1 doWork before...
ConcreteComponent doWork...
ConcreteDecorator1 doWork after...ConcreteDecorator2 doWork before...
ConcreteDecorator1 doWork before...
ConcreteComponent doWork...
ConcreteDecorator1 doWork after...
ConcreteDecorator2 doWork after... 
pConcreteComponent和pConcreteDecorator1还比较简单,pConcreteDecorator2装饰器就比较复杂了,就好像是在pConcreteDecorator1的基础上套了一个壳,从这点也可以看出装饰器模式结构可以非常的灵活。
3.2.示例2:函数装饰器
虽然装饰器模式通常用于类,但是同样可以应用于函数。例如,假设代码中有一个特定的函数给你带来了麻烦:你希望记录该函数被调用的所有时间,并在Excel中分析统计信息。当然,这可以通过在调用函数前后放置一些代码来实现,即
cout<< "Entering function XYz\n":
// do the work
cout <<"Exiting function XYZ\n"; 
        这很好,但从关注点分离的角度来看并不好:我们确实希望将日志功能存储在某个地方,以便复用并在必要时强化它。
         有不同的方法可以实现这一点。一种方法是简单地将整个工作单元作为一个函数提供给某些日志组件:
struct Logger{function<void()> func;string name;
public:Logger(const function<void()>& func, const string& name):func{func},name{name}{}void operator()()const {cout<< "Entering "<< name << "\n";func();cout<< "Exiting "<< name << "\n";}
}; 
使用这种方法,我们可以编写如下代码来使用它:
Logger([](){ cout << "Hello\n";},“HelloFunction")();// Entering HelloFunction
// Hello
// Exiting HelloFunction 
始终有一种选择,可以将函数作为模板参数而不是std::function传人。这导致与前面的结果略有不同:
template <typename Func>
struct Logger2{Func func;string name;
public:Logger2(const Func& func,const string& name):func{func},name{name}{}void operator()()const{cout << "Entering "<< name << endl;func();cout << "Exiting "<< name << endl;}
}; 
此实现的用途完全不变。我们可以创建一个工具函数来实际创建这样的记录器:
template <typename Func> 
auto make_logger2(Func func,const string& name)
{return logger2<Func>{ func, name };
} 
然后像下面这样使用它:
auto call = make_logger2([](){cout <<"Hello!" <<endl; },"HelloFunction");
call();// output same as before 
你可能会问 "这有什么意义?" 嗯……我们现在有能力在需要装饰某个函数时创建一个装饰器(其中包含装饰的函数)并调用它。
 现在,我们遇到了一个新的挑战:如果要记录调用函数add()的日志,该怎么办呢?此处将add()函数定义如下:
double add(double a, double b)
{cout << a << "+"<< b<<"="<<(a + b)<< endl;return a + b;
} 
需要 add()函数的返回值吗?如果需要的话,它将从 1ogger中返回(因为1ogger装饰了add()函数)。这并不容易!但也绝不是不可能。我们再次改进一下记录器:
template <typename R, typename... Args>
struct Logger3<R(Args...)>
{Logger3(function<R(Args...)>func, const string& name): func{func},name{name}{}R operator()(Args ...args){    cout << "Entering"<< name << endl;R result = func(args...);cout <<"Exiting "<< name << endl;return result;}function<R(Args ...)> func;string name;
}; 
在上述代码中,模板参数R表示返回值的类型,Args表示丽数的参数类型。与之前相同的是,装饰器在必要时调用函数;唯一的区别是opeIator() 返回了一个R,因此采用方法不会丢失返回值。
         我们可以构造另一个工具函数 make_:
template <typename R, typename... Args>
auto make_1ogger3(R(*func)(Args...), const string& name)
{return Logger3<R(Args...)>(function<R(Args...)>(func)name);
} 
请注意,这里没有使用std::function,而是将第一个参数定义为普通函数指针,我们现在可以使用此函数实例化日志调用并使用它:
auto logged add = make logger3(add, "Add");
auto result=logged add(2,3); 
当然,make_logger3可以被依赖注入取代。这种方法的好处是能够:
 1)通过提供空对象而不是实际的记录器,动态地打开和关闭日志记录 。
 2)禁用记录器正在记录的代码的实际调用(同样,通过替换其他记录器)。
 总之,对于开发人员而言,这是一个有用的工具函数。
4.总结
使用装饰器模式的好处:
1)可以在运行时动态的添加或删除功能。
2)可以递归地组合多个装饰器,从而创建复杂的对象。
3)可以使用不同的装饰器组合来创建不同的行为。
4)可以在不修改原有代码的情况下增加新功能。
总体来说,装饰器模式是一种强大且灵活的设计模式,它可以用于创建可扩展、可定制和可组合的对象。
相关文章:
设计模式之装饰器模式
面向对象设计原则 接口隔离原则:面向对象设计之接口隔离原则-CSDN博客 设计模式 工厂模式 : 设计模式之工厂模式-CSDN博客 迭代器模式:设计模式之迭代器模式-CSDN博客 适配器模式:设计模式之适配器模式-CSDN博客 过滤器模式&#…...
【Java万花筒】缓存与存储:Java应用中的数据处理利器
激发性能之源:深度剖析Java开发中的五大数据缓存与存储方案 前言 在现代软件开发中,高效地处理和存储数据是至关重要的任务。本文将介绍一系列在Java应用中广泛使用的数据缓存与存储库,涵盖了Ehcache、Redisson、Apache Cassandra、Hazelca…...
解决nodejs报错内存泄漏问题,项目无法运行
解决方法一 一、使用 increase-memory-limit npm install increase-memory-limit //本项目中使用// 或 npm install -g increase-memory-limit //全局安装二、安装 npm install --save cross-env 配置package.json文件 LINMIT大小 81928g "scripts": {"f…...
计算机网络-物理层基本概念(接口特性 相关概念)
文章目录 总览物理层接口特性星火模型给出的相关概念解释(仅供参考) 总览 求极限传输速率:奈氏准则,香农定理(背景环境不一样) 编码:数据变成数字信号 调制:数字信号变成模拟信号 信…...
从规则到神经网络:机器翻译技术的演化之路
文章目录 从规则到神经网络:机器翻译技术的演化之路一、概述1. 机器翻译的历史与发展2. 神经机器翻译的兴起3. 技术对现代社会的影响 二、机器翻译的核心技术1. 规则基础的机器翻译(Rule-Based Machine Translation, RBMT)2. 统计机器翻译&am…...
python 面经
关于自身特点 1. 介绍下自己,讲一下在公司做的项目 2. 说一下熟悉的框架,大致讲下其特点 python 基础 1.可变与不可变类型区别 2.请解释join函数 3.请解释*args和**kwargs的含义,为什么使用* args,** kwargs? 4.解释…...
Ubuntu (Linux) 下创建软链接(即符号链接,相当于windows下的快捷方式)方法
Ubuntu (Linux) 下创建软链接(即符号链接,相当于windows下的快捷方式)方法 使用创建软链接的命令 #命令格式如下。注意:请使用绝对路径,否则链接可能失效 ln -s <源文件或目录的绝对路径> <符号链接文件&am…...
LeetCode.2765. 最长交替子数组
题目 2765. 最长交替子数组 分析 为了得到数组 nums 中的最长交替子数组的长度,需要分别计算以每个下标结尾的最长交替子数组的长度。为了方便处理,计算过程中需要考虑长度等于 1 的最长交替子数组,再返回结果时判断最长交替子数组的长度…...
Springboot日志框架logback与log4j2
目录 Springboot日志使用 Logback日志 日志格式 自定义日志格式 日志文件输出 Springboot启用log4j2日志框架 Springboot日志使用 Springboot底层是使用slf4jlogback的方式进行日志记录 Logback日志 trace:级别最低 debug:调试级别的,…...
浪花 - 用户信息展示+更新
1. 用户登录获取登录凭证 已登录的用户才能获取个人信息发送 Aixos 请求登录 const user ref();onMounted(async () > {const res await myAxios.get(/user/current);if (res.code 0) {console.log("获取用户信息成功");user.value res.data;} else {consol…...
xxe漏洞之scms靶场漏洞
xxe-scms 代码审核 (1)全局搜索simplexml_load_string simplexml_load_string--将XML字符串解释为对象 (2)查看源代码 ID1 $GLOBALS[HTTP_RAW_POST_DATA]就相当于file_get_contents("php://input"); 因此这里就存…...
Unity3d C#实现三维场景中图标根据相机距离动态缩放功能
前言 如题的需求,其实可以通过使用UI替代场景中的图标来实现,不过这样UI的处理稍微麻烦,而且需要在图标上添加粒子特效使用SpriteRender更方便快捷。这里就根据相机离图标的位置来计算图标的缩放大小即可。这样基本保持了图标的大小…...
Linux网络编程(二-套接字)
目录 一、背景知识 1.1 端口号 1.2 网络字节序 1.3 地址转换函数 二、Socket简介 三、套接字相关的函数 3.1 socket() 3.2 bind() 3.3 connect() 3.4 listen() 3.5 accept() 3.6 read()/recv()/recvfrom() 3.7 send()/sendto() 3.8 close() 四、UPD客服/服务端实…...
【DeepLearning-1】 注意力机制(Attention Mechanism)
1.1注意力机制的基本原理: 计算注意力权重: 注意力权重是通过计算输入数据中各个部分之间的相关性来得到的。这些权重表示在给定上下文下,数据的某个部分相对于其他部分的重要性。 加权求和: 使用这些注意力权重对输入数据进行加权…...
c++:string相关的oj题(415. 字符串相加、125. 验证回文串、541. 反转字符串 II、557. 反转字符串中的单词 III)
文章目录 1. 415. 字符串相加题目详情代码1思路1代码2思路2 2. 125. 验证回文串题目详情代码1(按照要求修改后放到新string里)思路1代码2(利用双指针/索引)思路2 3. 541. 反转字符串 II题目详情代码1思路1 4. 557. 反转字符串中的单词 III题目详情代码1&…...
HuoCMS|免费开源可商用CMS建站系统HuoCMS 2.0下载(thinkphp内核)
HuoCMS是一套基于ThinkPhp6.0Vue 开发的一套HuoCMS建站系统。 HuoCMS是一套内容管理系统同时也是一套企业官网建设系统,能够帮过用户快速搭建自己的网站。可以满足企业站,外贸站,个人博客等一系列的建站需求。HuoCMS的优势: 可以使用统一后台…...
VsCode + CMake构建项目 C/C++连接Mysql数据库 | 数据库增删改查C++封装 | 信息管理系统通用代码 ---- 课程笔记
这个是B站Up主:程序员程子青的视频 C封装Mysql增删改查操作_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1m24y1a79o/?p6&spm_id_frompageDriver&vd_sourcea934d7fc6f47698a29dac90a922ba5a3安装mysql:mysql 下载和安装和修改MYSQL8.0 数据库存储…...
HackTheBox - Medium - Linux - Ransom
Ransom 外部信息搜集 端口扫描 循例nmap Web枚举 /api/login 它似乎受nosql注入影响,我们能够登录成功 把返回的cookie丢到cookie editor,回到主页 zip是加密的 Foothold 我们可以得知加密类型是ZipCrypto 谷歌能够找到这篇文章,它将告诉我…...
柠檬微趣面试准备
简单介绍一下spring原理 Spring框架是一个开源的Java应用程序框架,它提供了广泛的基础设施支持,帮助开发者构建Java应用程序。Spring的设计原则包括依赖注入(DI)和面向切面编程(AOP)等,以促使代…...
uniapp嵌套webview,无法返回上一级?
uniapp嵌套webview,如何解决回退问题? 文章目录 uniapp嵌套webview,如何解决回退问题?遇到问题解决方式方式一方式二 场景: 进入首页,自动跳转第三方应用 遇到问题 在设备上运行时,无法回退上…...
XML Group端口详解
在XML数据映射过程中,经常需要对数据进行分组聚合操作。例如,当处理包含多个物料明细的XML文件时,可能需要将相同物料号的明细归为一组,或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码,增加了开…...
React 第五十五节 Router 中 useAsyncError的使用详解
前言 useAsyncError 是 React Router v6.4 引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误:捕获在 loader 或 action 中发生的异步错误替…...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...
376. Wiggle Subsequence
376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...
智能在线客服平台:数字化时代企业连接用户的 AI 中枢
随着互联网技术的飞速发展,消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁,不仅优化了客户体验,还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用,并…...
什么是库存周转?如何用进销存系统提高库存周转率?
你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...
WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)
一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解,适合用作学习或写简历项目背景说明。 🧠 一、概念简介:Solidity 合约开发 Solidity 是一种专门为 以太坊(Ethereum)平台编写智能合约的高级编…...
06 Deep learning神经网络编程基础 激活函数 --吴恩达
深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...
根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:
根据万维钢精英日课6的内容,使用AI(2025)可以参考以下方法: 四个洞见 模型已经比人聪明:以ChatGPT o3为代表的AI非常强大,能运用高级理论解释道理、引用最新学术论文,生成对顶尖科学家都有用的…...
