C++设计模式(装饰模式)

news2025/1/14 16:55:34

一、介绍

1.动机

在某些情况下我们可能会“过度地使用继承来扩展对象的功能”,由于继承为类型引入的静态特质,使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合) 会导致更多子类的膨胀。

如何使“对象功能的扩展”能够根据需要来动态地实现?同时避免“扩展功能的增多”带来的子类膨胀问题,从而使得任何“功能扩展变化”所导致的影响将为最低?

 

2.定义

动态(组合)地给一个对象增加一些额外的职责。就增加功能而言,Decorator模式比生成子类(继承) 更为灵活(消除重复代码并减少子类个数)。——GOF

 

3.结构图

0167c308773d43d48182a8d411022685.jpeg

 

4.要点总结

通过采用组合而非继承的手法,Decorator模式实现了在运行时动态扩展对象功能的能力,而且可以根据需要扩展多个功能,避免了使用继承带来的“灵活性差”和“多子类衍生问题”。

Decorator类在接口上表现为is-a的继承关系,即Decorator类继承了Component类所具有的接口;但在实现上又表现为has-a的组合关系,即Decorator类又使用了另外一个Component类。

Decorator模式的目的并非解决“多子类衍生的多继承”问题,其应用的要点在于解决“主体类在多个方向上的扩展功能”——是为“装饰”的含义。

 

 

二、装饰模式

1.概念

装饰模式允许在不改变现有对象结构的情况下,动态地为对象添加额外的功能。

①装饰模式的优点:

  • 提供了比继承更灵活的功能扩展机制。
  • 通过使用不同的具体装饰类可以创造出很多不同行为的组合。

②装饰模式的缺点:

  • 由于需要引入更多的类和对象,可能会增加系统的复杂度。
  • 如果装饰器的层次结构设计不当,可能会导致维护困难。

 

2.实现要点

装饰模式实现要点:

  • 抽象构件:定义了对象的接口,所有的具体构件都必须继承或实现这个接口。
  • 具体构件:实现抽象构件的具体类,它是被装饰的对象。
  • 抽象装饰:继承并组合抽象构件,可以通过其子类扩展具体构件的功能。
  • 具体装饰:实现抽象装饰的相关方法,可传入具体构件对象以扩展其功能。

抽象装饰器使用组合是为了传入主体来实现装饰,使用继承是为了规范接口,同时保证包装后不改变原对象的本质。

 

3.示例

//抽象构件
class Coffee {
public:
	virtual string getName() = 0;
	virtual double cost() = 0;
	virtual ~Coffee() {}

};

//具体构件
class Espresso :public Coffee {
public:
	virtual string getName() override {
		return "Espresso";
	}
	virtual double cost() override {
		return 2.5;
	}
};

//具体构件
class Americano :public Coffee {
public:
	virtual string getName() override {
		return "Americano";
	}
	virtual double cost() override {
		return 3.0;
	}
};

//具体构件
class Latte :public Coffee {
public:
	virtual string getName() override {
		return "Latte";
	}
	virtual double cost() override {
		return 3.5;
	}
};

//抽象装饰器
class CoffeeDecorator :public Coffee {
protected:
	Coffee* coffee;
public:
	CoffeeDecorator(Coffee* cof) :coffee(cof) {
	}
	virtual ~CoffeeDecorator() {
		delete coffee;
	}
};

//具体装饰器
class SugarDecorator : public CoffeeDecorator {
public:
	SugarDecorator(Coffee* cof) :CoffeeDecorator(cof) {}
	virtual string getName() override {
		return coffee->getName() + " Sugar";
	}
	virtual double cost() override {
		return coffee->cost() + 0.2;
	}
};

//具体装饰器
class MilkDecorator :public CoffeeDecorator {
public:
	MilkDecorator(Coffee* cof) :CoffeeDecorator(cof) {}
	virtual string getName() override {
		return coffee->getName() + " Milk";
	}
	virtual double cost() override {
		return coffee->cost() + 0.5;
	}
};

//具体装饰器
class CreamDecorator :public CoffeeDecorator {
public:
	CreamDecorator(Coffee* cof): CoffeeDecorator(cof) {}
	virtual string getName() override {
		return coffee->getName() + " Cream";
	}
	virtual double cost() override {
		return coffee->cost() + 0.8;
	}
};

测试代码:

Coffee* espresso = new Espresso();
cout << espresso->getName() << " " << espresso->cost() << endl;
espresso = new SugarDecorator(espresso);
cout << espresso->getName() << " " << espresso->cost() << endl;
espresso = new MilkDecorator(espresso);
cout << espresso->getName() << " " << espresso->cost() << endl;
delete espresso;

Coffee* americano = new Americano();
cout << americano->getName() << " " << americano->cost() << endl;
Coffee* americano1 = new SugarDecorator(americano);
cout << americano1->getName() << " " << americano1->cost() << endl;
Coffee* americano2 = new CreamDecorator(americano1);
cout << americano2->getName() << " " << americano2->cost() << endl;
delete americano2;

Coffee* latte = new Latte();
cout << latte->getName() << " " << latte->cost() << endl;
MilkDecorator* milk = new MilkDecorator(latte);
cout << milk->getName() << " " << milk->cost() << endl;
CreamDecorator* cream = new CreamDecorator(milk);
cout << cream->getName() << " " << cream->cost() << endl;
delete cream;

输出结果:

Espresso 2.5
Espresso Sugar 2.7
Espresso Sugar Milk 3.2
Americano 3
Americano Sugar 3.2
Americano Sugar Cream 4
Latte 3.5
Latte Milk 4
Latte Milk Cream 4.8

 

 

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

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

相关文章

【VMware】Ubuntu 虚拟机硬盘扩容教程(Ubuntu 22.04)

引言 想装个 Anaconda&#xff0c;发现 Ubuntu 硬盘空间不足。 步骤 虚拟机关机 编辑虚拟机设置 扩展硬盘容量 虚拟机开机 安装 gparted sudo apt install gparted启动 gparted sudo gparted右键sda3&#xff0c;调整分区大小 新大小拉满 应用全部操作 调整完成

03-12、SpringCloud Alibaba第十二章,升级篇,服务注册与配置中心Nacos

SpringCloud Alibaba第十二章&#xff0c;升级篇&#xff0c;服务注册与配置中心Nacos 一、为什么SpringCloud Alibaba 1、为什么 有了spring cloud这个微服务的框架&#xff0c;为什么又要使用spring cloud alibaba这个框架了&#xff1f;最重要的原因在于spring cloud中的…

java网络通信(三):TCP通信、实现客户端-服务端消息通信

目录 1、什么是 TCP协议&#xff1f; 2、代码实现TCP协议的一发一收 2.1、客户端 2.2、服务端 2.3 结果演示 3、代码实现TCP协议的多发多收 3.1 客户端 3.2 服务端 3.3 结果演示 简介&#xff1a;本文章主要是演示如何用java代码以及TCP协议实现网络通信&#xff0c;实…

剖析go协程池实现原理

go协程池实现 在go语言编程中有一种池肯定避免不了&#xff0c;那就是-协程池&#xff0c;无论你是日常工作还是面试中面试官都无法避免协程池&#xff0c;掌握协程池你也就算是入门go的并发编程了&#xff0c;打一波广告后面会有专门的文章来介绍如何在go中进行并发编程。 协…

华为关键词覆盖应用市场ASO优化覆盖技巧

在我国的消费者群体当中&#xff0c;华为的品牌形象较高&#xff0c;且产品质量过硬&#xff0c;因此用户基数也大。与此同时&#xff0c;随着影响力的增大&#xff0c;华为不断向外扩张&#xff0c;也逐渐成为了海外市场的香饽饽。作为开发者和运营者&#xff0c;我们要认识到…

万能门店小程序管理系统 onepic_uploade 任意文件上传漏洞复现

0x01 产品简介 万能门店小程序管理系统是一款功能强大的工具,旨在为各行业商家提供线上线下融合的全方位解决方案。是一个集成了会员管理和会员营销两大核心功能的综合性平台。它支持多行业使用,通过后台一键切换版本,满足不同行业商家的个性化需求。该系统采用轻量后台,搭…

QT:信号和槽01

QT中什么是信号和槽 概念解释 在 Qt 中&#xff0c;信号&#xff08;Signals&#xff09;和槽&#xff08;Slots&#xff09;是一种用于对象间通信的机制。信号是对象发出的事件通知&#xff0c;而槽是接收并处理这些通知的函数。 例如&#xff0c;当用户点击一个按钮时&#…

SQL面试50题

数据表关系图 数据表 CREATE TABLE student (id int(11) NOT NULL AUTO_INCREMENT,name varchar(255) NOT NULL,sex enum(female,male) NOT NULL,birth date NOT NULL,credit float(5,2) DEFAULT NULL,PRIMARY KEY (id) ) ENGINEInnoDB AUTO_INCREMENT25 DEFAULT CHARSETutf8;…

下载maven 3.6.3并校验文件做md5或SHA512校验

一、下载Apache Maven 3.6.3 Apache Maven 3.6.3 官方下载链接&#xff1a; 二进制压缩包&#xff08;推荐&#xff09;: ZIP格式: https://archive.apache.org/dist/maven/maven-3/3.6.3/binaries/apache-maven-3.6.3-bin.zipTAR.GZ格式: https://archive.apache.org/dist/…

基于poi和javabean的excel读取

写在前面 示例写出时间&#xff1a;2024-12-02 这仅仅是excel读取的一个示例, 记录一下&#xff0c;这里也改了一下之前的导出&#xff0c;主要是为了兼容读取 之前的博客地址 基于poi和JavaBean的excel导出 poi依赖 <dependency><groupId>org.apache.poi</gr…

一键生成后端服务,MemFire Cloud重新定义开发效率

作为开发者&#xff0c;特别是独立开发者和小团队成员&#xff0c;大家都知道开发的最大难题之一就是搭建后端服务。要让一个应用从零开始&#xff0c;除了前端的开发工作外&#xff0c;还需要考虑数据库、接口、认证、存储等等一系列繁琐的后台工作。而MemFire Cloud这款神器&…

Maven、JAVAWeb、Servlet

知识点目标 1、MavenMaven是什么Maven项目的目录结构Maven的Pom文件Maven的命令Maven依赖管理Maven仓库JavaWeb项目 2.网络基础知识 3、ServletMaven Maven是什么 Maven是Java的项目管理工具&#xff0c;可以构建&#xff0c;打包&#xff0c;部署项目&#xff0c;还可以管理…

controller中的参数注解@Param @RequestParam和@RequestBody的不同

现在controller中有个方法&#xff1a;&#xff08;LoginUserRequest是一个用户类对象&#xff09; PostMapping("/test/phone")public Result validPhone(LoginUserRequest loginUserRequest) {return Result.success(loginUserRequest);}现在讨论Param("login…

Linux 内核系统架构

Linux 内核是一个复杂且高度模块化的系统&#xff0c;负责操作硬件资源、管理进程和内存、提供网络服务、执行文件系统操作、进行设备驱动程序的管理等。它为用户空间提供了一个抽象层&#xff0c;并为应用程序提供了底层服务。本文将深入探讨 Linux 内核的系统架构&#xff0c…

AI开发:逻辑回归 - 实战演练- 垃圾邮件的识别(二)

接上一篇AI开发&#xff1a;逻辑回归 - 实战演练- 垃圾邮件的识别&#xff08;一&#xff09; new_email 无论为什么文本&#xff0c;识别结果几乎都是垃圾邮件,因此我们需要对源码的逻辑进行梳理一下&#xff1a; 在代码中&#xff0c;new_email 无论赋值为何内容都被识别为…

WPF+MVVM案例实战与特效(三十)- 封装一个系统日志显示控件

文章目录 1、运行效果2、日志控件封装1、文件创建2、DisplayLogPanel.xaml 代码3、DisplayLogPanel.cs 代码4、数据模型5、枚举类型3、自定义控件使用1、LogPanelWindow.xaml2、LogPanelViewModel.cs4、总结1、运行效果 2、日志控件封装 1、文件创建 打开 Wpf_Examples ,在 …

VideoBooth: Diffusion-based Video Generation with Image Prompts

VideoBooth: Diffusion-based Video Generation with Image Prompts 概括 文章提出了一个视频生成模型VideoBooth&#xff0c;输入一张图片和一个文本提示词&#xff0c;即可输出保持图片中物体且符合文本提示词要求的视频。 方法 粗-细两阶段设计&#xff1a;1&#xff09;…

电子电气架构 --- 面向服务的汽车诊断架构

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 所有人的看法和评价都是暂时的,只有自己的经历是伴随一生的,几乎所有的担忧和畏惧,都是来源于自己的想象,只有你真的去做了,才会发现有多快乐。…

生成树详解(STP、RSTP、MSTP)

目录 1、STP 1.概述 2.基本概念 3.端口角色及其作用 4.报文结构 5.STP的端口状态 6.三种定时器 7.STP选举步骤 8.配置BPDU的比较原则 9.TCN BPDU 10.临时环路的问题 11.传统STP的不足 拓扑变更处理过程 2、RSTP 1.端口角色 2.端口状态 3.P/A&#xff08;Propo…

基于Python制作一个简易UI界面

基于Python制作一个简易UI界面 目录 基于Python制作一个简易UI界面1 原理简介2 编写程序3 程序测试 1 原理简介 这里用到了Python自带的UI库tkinter。 tkinter 是 Python 的标准 GUI&#xff08;图形用户界面&#xff09;库&#xff0c;用于创建和管理图形界面。它提供了一个简…