【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.rhkb.cn/news/114366.html

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

相关文章

基于YOLOV8模型和CCPD数据集的车牌目标检测系统(PyTorch+Pyside6+YOLOv8模型)

摘要&#xff1a;基于YOLOV8模型和CCPD数据集的车牌目标检测系统可用于日常生活中检测与定位车牌目标&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的目标检测&#xff0c;另外本系统还支持图片、视频等格式的结果可视化与结果导出。本系统采用YOLOv8目标检测算…

【论文阅读】自动驾驶中车道检测系统的物理后门攻击

文章目录 AbstractIntroduction 论文题目&#xff1a; Physical Backdoor Attacks to Lane Detection Systems in Autonomous Driving&#xff08;自动驾驶中车道检测系统的物理后门攻击&#xff09; 发表年份&#xff1a; 2022-MM&#xff08;ACM International Conference on…

Centos 7.6 安装mongodb

以下是在CentOS 7.6上安装MongoDB的步骤&#xff1a; 打开终端并以root用户身份登录系统。 创建一个新的MongoDB存储库文件 /etc/yum.repos.d/mongodb-org-4.4.repo 并编辑它。 sudo vi /etc/yum.repos.d/mongodb-org-4.4.repo在编辑器中&#xff0c;添加下面的内容到文件中并…

【广州华锐互动】综合管廊3D可视化管理系统有效解决城市公用设施管理问题

在过去的几十年中&#xff0c;城市化进程不断加速&#xff0c;城市规模不断扩大&#xff0c;人口密度不断增加。这种发展带来了对城市基础设施的巨大需求&#xff0c;尤其是对电力、水、燃气和通信等公用设施的管理和维护。 为了满足这些需求&#xff0c;许多城市开始建设和管理…

Opencv基于文字检测去图片水印

做了一个简单的去水印功能&#xff0c;基于文字检测去图片水印。效果如下&#xff1a; 插件功能代码参考如下&#xff1a; using namespace cv::dnn; TextDetectionModel_DB *textDetector0; void getTextDetector() {if(textDetector)return;String modelPath "text_de…

【Redis】Redis 的学习教程(六)Redis 的缓存问题

在服务端中&#xff0c;数据库通常是业务上的瓶颈&#xff0c;为了提高并发量和响应速度&#xff0c;我们通常会采用 Redis 来作为缓存&#xff0c;让尽量多的数据走 Redis 查询&#xff0c;不直接访问数据库。 同时 Redis 在使用过程中&#xff08;高并发场景下&#xff09;也…

Ansible-palybook学习

目录 一.playbook介绍二.playbook格式1.书写格式2.notify介绍 一.playbook介绍 playbook 是 ansible 用于配置&#xff0c;部署&#xff0c;和管理被控节点的剧本。通过 playbook 的详细描述&#xff0c;执行其中的一系列 tasks &#xff0c;可以让远端主机达到预期的状态。pl…

uniapp项目实战系列(3):底部导航栏与头部导航栏的配置

目录 系列往期文章&#xff08;点击跳转&#xff09;uniapp项目实战系列(1)&#xff1a;导入数据库&#xff0c;启动后端服务&#xff0c;开启代码托管&#xff08;点击跳转&#xff09;uniapp项目实战系列(2)&#xff1a;新建项目&#xff0c;项目搭建&#xff0c;微信开发工具…

Mac性能优化:深入了解WindowServer及其影响

文章目录 Mac性能优化:深入了解WindowServer及其影响WindowServer是什么?WindowServer为什么会占用那么多CPU?如何检查WindowServer是否使用了过多的CPU使用率?如何减少WindowServer的CPU使用率?Mac性能优化:深入了解WindowServer及其影响 大家好!今天我们来聊聊Mac上的…

【OJ比赛日历】快周末了,不来一场比赛吗? #09.03-09.09 #12场

CompHub[1] 实时聚合多平台的数据类(Kaggle、天池…)和OJ类(Leetcode、牛客…&#xff09;比赛。本账号会推送最新的比赛消息&#xff0c;欢迎关注&#xff01; 以下信息仅供参考&#xff0c;以比赛官网为准 目录 2023-09-03&#xff08;周日&#xff09; #5场比赛2023-09-04…

函数(个人学习笔记黑马学习)

1、函数定义 #include <iostream> using namespace std;int add(int num1, int num2) {int sum num1 num2;return sum; }int main() {system("pause");return 0; } 2、函数的调用 #include <iostream> using namespace std;int add(int num1, int num2…

分布式锁实现一. 利用Mysql数据库update锁

文章目录 分布式锁1、什么是分布式锁&#xff1a;2、分布式锁应该具备哪些条件&#xff1a; 基于数据库的分布式锁代码传送代码运行 分布式锁 1、什么是分布式锁&#xff1a; 分布式锁&#xff0c;即分布式系统中的锁。在单体应用中我们通过锁解决的是控制共享资源访问的问题…

常见的数据结构之队列

一、介绍 队列(Queue)是一种常见的数据结构,用于存储和管理一系列数据元素,其中元素按照 先进先出(First-In-First-Out,简称FIFO)的原则进行插入和删除。 队列可以类比为现实生活中排队等候的场景,例如在超市收银台排队购物的顾客队列。 二、队列的基本操作 2.1 出…

PHP8的箭头函数-PHP8知识详解

php 7.4 引入了箭头函数&#xff08;Arrow Functions&#xff09;&#xff0c;并在 PHP 8 中得到了进一步改进和扩展。 箭头函数是一种更简洁的匿名函数形式&#xff0c;它们提供了一种更便捷的方式来定义轻量级的、单行的回调函数。 箭头函数的语法如下&#xff1a; fn (参…

Docker拉取RocketMQ及可视化界面

本文介绍Docker拉取RocketMQ及可视化界面操作步骤 Linux下安装Docker请参考&#xff1a;Linux安装Docker 文章目录 安装namesrv创建挂载目录授权相关权限拉取镜像运行容器查看运行情况 安装Broker创建挂载目录及配置文件目录授权相关权限创建配置文件运行容器查看运行情况 安装…

2023年8月随笔之有顾忌了

1. 回头看 日更坚持了243天。 读《发布&#xff01;设计与部署稳定的分布式系统》终于更新完成 选读《SQL经典实例》也更新完成 读《高性能MySQL&#xff08;第4版&#xff09;》开更&#xff0c;但目前暂缓 读《SQL学习指南&#xff08;第3版&#xff09;》开更并持续更新…

KaiwuDB 助力能源企业实现 4 大价值提升

行业背景 近年来&#xff0c;随着能源行业数字化的不断推进&#xff0c;智能电网、可再生能源发电、分布式发电、微电网等技术蓬勃发展。越来越多的能源企业意识到数据管理与价值挖掘对储能及能源利用有着重大意义&#xff0c;并开始探索一套有效的数据库解决方案以应对分布式…

Redis 的混合持久化

RDB 相比于 AOF&#xff0c;数据恢复的速度更快&#xff0c;因为是二进制数据&#xff0c;直接加载进内存即可&#xff0c;但是 RDB 的频率不好把握。 如果频率太低&#xff0c;在两次快照期间服务器发生宕机&#xff0c;可能会丢失较多的数据如果频率太高&#xff0c;频繁写入…

Apipost:API文档、调试、Mock与测试的一体化协作平台

随着数字化转型的加速&#xff0c;API&#xff08;应用程序接口&#xff09;已经成为企业间沟通和数据交换的关键。而在API开发和管理过程中&#xff0c;API文档、调试、Mock和测试的协作显得尤为重要。Apipost正是这样一款一体化协作平台&#xff0c;旨在解决这些问题&#xf…

Linux以系统服务的方式启动Kafka(其他服务同理)

最终效果&#xff1a; 先回顾命令行的启动方式&#xff1a; kafka的启动 进入kafka的安装目录 1、首先启动zookeeper服务&#xff1a; bin/zookeeper-server-start.sh config/zookeeper.properties2、再启动kafka bin/kafka-server-start.sh config/server.properties &…