【设计模式】装饰模式

六、装饰模式

装饰(Decorator) 模式也称为装饰器模式/包装模式,是一种结构型模式。这是一个非常有趣和值得学习的设计模式,该模式展现出了运行时的一种扩展能力,以及比继承更强大和灵活的设计视角和设计能力,甚至在有些场合下,不使用该模式很难解决问题。
在本模式的讲解过程中,会提及类与类之间的继承关系和组合关系,还会引出面向对象程序设计的一个重要原则——组合复用原则。

6.1 问题的提出

继续前面的闯关打斗类游戏。在游戏中,不但会出现各种人物、怪物,还会出现许多UI (用户接口)界面。例如,主角一般会随身携带背包,背包中的每个格子用于放置一个物品,根据策划的规定,背包中格子数量比较多时,还会在背包右侧显示出滚动条,如图6.1所示。

在这里插入图片描述

再例如,有时需要显示一些公告信息,如图6.2所示。当公告信息太长时,也可能会出现滚动条等。
图6.1和图6.2这些看得见的UI界面元素称为控件。例如,要向其中输入文字可以使用文本控件,要显示多行信息可以使用列表控件等,当要显示的内容过长或者过宽时会显示出滚动条控件。

在这里插入图片描述

这里就以一个最简单的控件——列表控件为例,说明如何丰富该控件上所显示的内容,图6.3中第1幅子图是一个普通的列表控件,第2幅图增加了边框让其更有立体感,第3幅图增加了一个垂直滚动条,而第4幅图又增加了一个水平滚动条。

在这里插入图片描述

传统继承方案的问题

  1. 子类膨胀:新增功能(如阴影、外发光)需不断创建子类。
  2. 灵活性差:无法灵活组合功能(如无框但有垂直滚动条的控件)。

解决方案思路

采用组装方式动态添加功能:

  1. 基础控件(ListCtrl)作为核心。
  2. 附加功能(边框、滚动条)作为装饰器,通过组合方式叠加。

6.2 引入装饰模式

组合复用原则(CRP)

核心思想:优先使用组合而非继承,以降低类间耦合,避免父类代码冗余。

装饰模式实现代码

1. 抽象构件(Control)
// 抽象的控件类
class Control {
public:virtual void draw() = 0;
public:virtual ~Control() {}
};
2. 具体构件(ListCtrl)
// 列表控件类
class ListCtrl : public Control {
public:virtual void draw() {cout << "  绘制普通的列表控件!" << endl;}
};
3. 抽象装饰器(Decorator)
// 抽象的装饰器类
class Decorator : public Control {
public:Decorator(Control* tmpctrl) : m_control(tmpctrl) {}  // 组合关系virtual void draw() {m_control->draw();  // 委托给被装饰对象}
private:Control* m_control;
};
4. 具体装饰器

边框装饰器

class BorderDec : public Decorator {
public:BorderDec(Control* tmpctrl) : Decorator(tmpctrl) {}virtual void draw() {Decorator::draw();  // 先绘制原内容drawBorder();       // 再绘制新增内容}
private:void drawBorder() { cout << "  绘制边框!" << endl; }
};

垂直滚动条装饰器

class VerScrollBarDec : public Decorator {
public:VerScrollBarDec(Control* tmpctrl) : Decorator(tmpctrl) {}virtual void draw() {Decorator::draw();drawVerScrollBar();}
private:void drawVerScrollBar() { cout << "  绘制垂直滚动条!" << endl; }
};

客户端代码示例

int main() {// 组装带边框和垂直滚动条的控件Control* base = new ListCtrl();Control* withBorder = new BorderDec(base);Control* final = new VerScrollBarDec(withBorder);final->draw();  // 输出:普通列表 → 边框 → 垂直滚动条cout << "------------------------" << endl;// 组装只带水平滚动条的控件Control* base2 = new ListCtrl();Control* withHor = new HorScrollBarDec(base2);withHor->draw();  // 输出:普通列表 → 水平滚动条// 释放资源(注意顺序)delete final;delete withBorder;delete base;delete withHor;delete base2;return 0;
}

装饰模式UML图

m_control
«abstract»
Control
+draw()
ListCtrl
+draw()
«abstract»
Decorator
-m_control: Control
+draw()
BorderDec
+draw()
-drawBorder()
VerScrollBarDec
+draw()
-drawVerScrollBar()
HorScrollBarDec
+draw()
-drawHorScrollBar()

模式角色

角色说明示例类
抽象构件定义统一接口Control
具体构件基础功能实现ListCtrl
抽象装饰器持有构件引用,定义装饰接口Decorator
具体装饰器实现具体装饰逻辑BorderDec

6.3 饮料价格计算范例

问题描述

  • 基础饮料:10元
  • 可选配料:砂糖(+1元)、牛奶(+2元)、珍珠(+2元)

实现代码

1. 抽象构件(Beverage)
class Beverage {
public:virtual int getPrice() = 0;virtual ~Beverage() {}
};
2. 具体构件(FruitBeverage)
class FruitBeverage : public Beverage {
public:int getPrice() override { return 10; }
};
3. 抽象装饰器(CondimentDecorator)
class CondimentDecorator : public Beverage {
protected:Beverage* beverage;
public:CondimentDecorator(Beverage* b) : beverage(b) {}
};
4. 具体装饰器

砂糖装饰器

class Sugar : public CondimentDecorator {
public:Sugar(Beverage* b) : CondimentDecorator(b) {}int getPrice() override { return beverage->getPrice() + 1; }
};

珍珠装饰器

class Bubble : public CondimentDecorator {
public:Bubble(Beverage* b) : CondimentDecorator(b) {}int getPrice() override { return beverage->getPrice() + 2; }
};

客户端代码

int main() {Beverage* drink = new FruitBeverage();drink = new Bubble(drink);  // 加珍珠(+2)drink = new Sugar(drink);   // 加砂糖(+1)cout << "总价格:" << drink->getPrice() << "元" << endl;  // 输出:13元delete drink;return 0;
}

饮料范例UML图

beverage
«abstract»
Beverage
+getPrice()
FruitBeverage
+getPrice()
«abstract»
CondimentDecorator
-beverage: Beverage
+getPrice()
Sugar
+getPrice()
Bubble
+getPrice()

总结:装饰模式通过组合实现动态功能扩展,避免继承导致的类爆炸,符合开闭原则,适用于需要灵活添加可选功能的场景。

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

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

相关文章

科技赋能安全:慧通测控的安全带全静态性能测试

汽车的广泛普及给人们的出行带来了极大便利&#xff0c;但交通事故频发也成为严重的社会问题。据世界卫生组织统计&#xff0c;全球每年约有 135 万人死于道路交通事故&#xff0c;而安全带在减少事故伤亡方面起着不可替代的作用。正确使用安全带可使前排驾乘人员的死亡风险降低…

猿大师中间件:如何在最新Chrome浏览器Web网页内嵌本地OCX控件?

OCX控件是ActiveX控件的一种&#xff0c;主要用于在网页中嵌入复杂的功能&#xff0c;如图形渲染、多媒体播放等&#xff0c;可是随着谷歌Chrome等主流浏览器升级&#xff0c;目前已经不支持微软调用ActiveX控件了&#xff0c;如果想调用OCX控件用IE浏览器或者国产双擎浏览器&a…

基于STC89C52的DS1621温度检测系统

摘要 本文设计了一种基于STC89C52单片机的DS1621温度检测系统&#xff0c;通过DS1621传感器采集温度数据&#xff0c;利用STC89C52单片机处理数据&#xff0c;并通过串口将温度信息发送至电脑显示。借助Proteus软件进行仿真验证&#xff0c;实现了温度检测系统的功能演示。该系…

创建自己的github.io

1、创建GitHub账号 GitHub地址&#xff1a;https://github.com/ 点击Sign up创建账号 如果已创建&#xff0c;点击Sign in登录 2、创建仓库 假设Owner为username&#xff0c;则Repository name为username.github.io说明&#xff1a; 1、Owner为用户名 2、Repository name为仓…

AI Agent中的MCP详解

一、协议定义与核心价值 MCP(Model Context Protocol,模型上下文协议)是由Anthropic公司于2024年11月推出的开放标准协议,其核心目标是通过建立统一接口规范,解决AI模型与外部系统集成效率低下的行业痛点。该协议通过标准化通信机制,使大型语言模型(LLM)能够无缝对接数…

【AI论文】DeepMesh:基于强化学习的自回归艺术家网格创建

摘要&#xff1a;三角形网格在3D应用中扮演着至关重要的角色&#xff0c;能够实现高效的操作和渲染。虽然自回归方法通过预测离散的顶点标记来生成结构化的网格&#xff0c;但它们往往受到面数限制和网格不完整性的约束。为了应对这些挑战&#xff0c;我们提出了DeepMesh框架&a…

基于ArcGIS和ETOPO-2022 DEM数据分层绘制全球海陆分布

第〇部分 前言 一幅带有地理空间参考、且包含海陆分布的DEM图像在研究区的绘制中非常常见&#xff0c;本文将实现以下图像的绘制 关键步骤&#xff1a; &#xff08;1&#xff09;NOAA-NCEI官方下载最新的ETOPO-2022 DEM数据 &#xff08;2&#xff09;在ArcGIS&#xff08;…

Unity | 游戏数据配置

目录 一、ScriptableObject 1.创建ScriptableObject 2.创建asset资源 3.asset资源的读取与保存 二、Excel转JSON 1.Excel格式 2.导表工具 (1)处理A格式Excel (2)处理B格式Excel 三、解析Json文件 1.读取test.json文件 四、相关插件 在游戏开发中,策划…

docker模拟Dos_SYN Flood拒绝服务攻击 (Ubuntu20.04)

目录 ✅ 一、实验环境准备&#xff08;3 个终端&#xff09; &#x1f449; 所以最终推荐做法&#xff1a; 2️⃣ 配置 seed-attacker 为攻击者&#xff0c;开启 telnet 服务&#xff1a; 3️⃣ 配置 victim-10.9.0.5 为受害者服务器&#xff0c;开启 telnet 客户端并监听&…

场外个股期权是什么?场外个股期权还能做吗?

场外个股期权指在非正式的交易场所&#xff0c;即场外市场上&#xff0c;老板们与特定对手方直接进行的个股期权交易。 场外期权为何被严监管&#xff1f; 场外个股期权指在非正式的交易场所&#xff0c;即场外市场上&#xff0c;老板们与特定对手方直接进行的个股期权交易&am…

vulnhub靶场【billu系列】之billu_b0x2靶机

前言 靶机&#xff1a;billu_b0x2靶机&#xff0c;IP地址为192.168.10.10 攻击&#xff1a;kali&#xff0c;IP地址为192.168.10.6 靶机和攻击机都采用VMware虚拟机&#xff0c;都采用桥接网卡模式 文章涉及的靶机及工具&#xff0c;都可以自行访问官网或者项目地址进行获取…

高性能边缘计算网关-高算力web组态PLC网关

高性能EG8200Pro边缘计算算力网关-超强处理能力 样机申请测试&#xff1a;免费测试超30天&#xff08;https://www.iotrouter.com/prototype/&#xff09; 产品主要特点和特色功能 设备概览与连接能力 设备型号&#xff1a;EG8200P。主要特点&#xff1a; 支持多种工业协议&am…

数据可信安全流通实战,隐语开源社区Meetup武汉站开放报名

隐语开源社区 Meetup 系列再出发&#xff01;2025 年将以武汉为始发站&#xff0c;聚焦"技术赋能场景驱动"&#xff0c;希望将先进技术深度融入数据要素流转的各个环节&#xff0c;推动其在实际应用场景中落地生根&#xff0c;助力释放数据要素的最大潜能&#xff01…

避坑指南 | 阿里云服务器centos7上MySQL部署优化指南

目录 1 检查阿里云是否安装mysql 1.1使用 rpm 命令 1.2检查 MySQL 服务状态 2 卸载mysql 2.1停止 MySQL 服务 2.2 检查已安装的 MySQL 包 2.3 卸载 MySQL 包 2.4 删除 MySQL 数据和配置文件 2.5 清理残留的依赖包 2.6 验证卸载 2.7 &#xff08;可选&#xff09;删除…

位运算--求二进制中1的个数

位运算–求二进制中1的个数 给定一个长度为 n 的数列&#xff0c;请你求出数列中每个数的二进制表示中 1 的个数。 输入格式 第一行包含整数 n。 第二行包含 n 个整数&#xff0c;表示整个数列。 输出格式 共一行&#xff0c;包含 n 个整数&#xff0c;其中的第 i 个数表…

Go语言的基础类型

一基础数据类型 一、布尔型&#xff08;Bool&#xff09; 定义&#xff1a;表示逻辑真 / 假&#xff0c;仅有两个值&#xff1a;true 和 false内存占用&#xff1a;1 字节使用场景&#xff1a;条件判断、逻辑运算 二、数值型&#xff08;Numeric&#xff09; 1. 整数类型&…

SpringBoot整合MQTT最详细版(亲测有效)

一、导入pom.xml依赖 <!--mqtt依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-integration</artifactId></dependency><dependency><groupId>org.springframework.in…

记一次发短信接口分析

忘记密码接口 数据包 GET /api/weatherforcast/user/send/17777777777 HTTP/2 Host: Cookie: SECKEY_ABVKd1GnERPtEFYSs7fL9W7VzoxAG0rjit7K8hAiMGIySpo522Wig70mdKRZQlvXNuqUTh9sBTWXG6XJ7miFZtA%3D%3D; Hm_lvt_018467e59f9d76a72cdbed870456819b1742445251,1742456927,1742…

dfs刷题排列问题 + 子集问题 + 组和问题总结

文章目录 一、排列问题全排列II题解代码 优美的排列题解代码 二、子集问题字母大小写全排列题解代码 找出所有子集的异或总和再求和题解代码 三、组合问题电话号码的字母组合题解代码 括号生成题解代码 组合题解代码 目标和题解代码 组合总和题解代码 总结 一、排列问题 全排列…

【AVRCP】蓝牙链路控制器(LC)与AVRCP互操作性要求深度解析

目录 一 、Link Controller&#xff08;LC&#xff09;概述 1.1 LC的定义与功能 1.2 LC在蓝牙技术中的重要性 二、Link Controller&#xff08;LC&#xff09;互操作性要求 2.1 互操作性要求概述 2.2 物理层互操作性要求 2.3 链路管理互操作性要求 2.4 其他互操作性要求…