当前位置: 首页 > news >正文

设计模式——装饰器模式

装饰器模式

装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。

装饰器模式通过将对象包装在装饰器类中,以便动态地修改其行为。

这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

我们通过下面的实例来演示装饰器模式的用法。其中,我们将把一个形状装饰上不同的颜色,同时又不改变形状类。

优缺点

优点

  1. 不改动原有代码,动态增加功能。
  2. 对象间不会相互依赖、松耦合。
  3. 符合开闭原则,扩展性好,便于维护。

缺点

  1. 装饰器环节过多的话,导致装饰器类膨胀。
  2. 装饰器层层嵌套比较复杂,可能导致排查问题流程繁琐。

装饰器模式的结构

通常情况下,扩展一个类的功能会使用继承方式来实现。但继承具有静态特征,耦合度高,并且随着扩展功能的增多,子类会很膨胀。如果使用组合关系来创建一个包装对象(即装饰对象)来包裹真实对象,并在保持真实对象的类结构不变的前提下,为其提供额外的功能,这就是装饰器模式的目标。下面来分析其基本结构和实现方法。

模式的结构

装饰器模式主要包含以下角色。

  1. 抽象构件(Component)角色:定义一个抽象接口以规范准备接收附加责任的对象。
  2. 具体构件(ConcreteComponent)角色:实现抽象构件,通过装饰角色为其添加一些职责。
  3. 抽象装饰(Decorator)角色:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
  4. 具体装饰(ConcreteDecorator)角色:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。

装饰器模式的结构图如图所示。

动图封面

装饰器模式实例:

实例——图画

不论一幅画有没有画框都可以挂在墙上,但是通常都是有画框的,并且实际上是画框被挂在墙上。在挂在墙上之前,画可以被蒙上玻璃,装到框子里;这时画、玻璃和画框形成了一个物体。

代码如下:

画(Painting接口)

public interface Painting {public void show();
}

唐宫仕女图(TangGong类)

public class TangGong implements Painting {@Overridepublic void show(){System.out.println("这是一副唐宫仕女图");}
}

装饰器类

public class Decorator implements Painting {private Painting painting;public Decorator(Painting monaLisa){this.painting = monaLisa;}@Overridepublic void show() {System.out.println("先加上相框");painting.show();System.out.println("再扣上玻璃");}
}

测试类

public class DecoratorTest {public static void main(String[] args) {Painting painting = new TangGong();Painting monaLisa = new Decorator(painting);TangGong.show();}
}

实现方式 ——蜜雪冰城奶茶

秋天到了,女朋友非要喝秋天的第一杯奶茶,到了“蜜雪冰城”奶茶店后,给女朋友点了一杯奶茶,加了珍珠、芒果等配料,给自己点了一杯加冰柠檬水,加了冰块、柠檬片等配料,这时候就可以使用装饰器模式。

奶茶:抽象构件
珍珠芒果奶茶、柠檬水:具体构件
配料:装饰角色
珍珠、芒果、柠檬:具体装饰角色

代码实现:

抽象构件(Component)角色:奶茶

public interface IMilktea {void addDosing();
}

具体构件(ConcreteComponent)角色:珍珠奶茶

public class PearlMilktea implements IMilktea{@Overridepublic void addDosing() {System.out.println("开始制作:珍珠奶茶");}
}

柠檬水

public class LemonMilktea implements IMilktea{@Overridepublic void addDosing() {System.out.println("开始制作:柠檬水");}
}

装饰(Decorator)角色:配料

public abstract  class Dosing implements IMilktea{IMilktea iMilktea;public Dosing(IMilktea iMilktea){this.iMilktea = iMilktea;}@Overridepublic void addDosing() {this.iMilktea.addDosing();}
}

具体装饰(ConcreteDecorator)角色:

加珍珠

public class Pearl extends Dosing {public Pearl(IMilktea iMilktea) {super(iMilktea);}@Overridepublic void addDosing() {super.addDosing();System.out.println("制作中:加珍珠");}
}

加芒果

public class Mango extends Dosing {public Mango(IMilktea iMilktea) {super(iMilktea);}@Overridepublic void addDosing() {super.addDosing();System.out.println("制作中:加芒果");}
}

加柠檬

public class Lemon extends Dosing {public Lemon(IMilktea iMilktea) {super(iMilktea);}@Overridepublic void addDosing() {super.addDosing();System.out.println("制作中:加柠檬");}
}

加冰

public class Ice extends Dosing {public Ice(IMilktea iMilktea) {super(iMilktea);}@Overridepublic void addDosing() {super.addDosing();System.out.println("制作中:加冰");}
}

客户端

public class Client {public static void main(String[] args) {System.out.println("服务员:你好,需要点什么呀?");System.out.println("我: 一杯加芒果、加珍珠的珍珠奶茶,一杯加柠檬、加冰的柠檬水");System.out.println("服务员:好的。");PearlMilktea pearlMilktea = new PearlMilktea();Pearl pearl = new Pearl(pearlMilktea);Mango mango = new Mango(pearl);Ice ice = new Ice(mango);ice.addDosing();System.out.println("第一杯制作完成");LemonMilktea lemonMilktea = new LemonMilktea();Lemon lemon = new Lemon(lemonMilktea);Ice ice1 = new Ice(lemon);ice1.addDosing();System.out.println("第二杯制作完成");System.out.println("我:珍珠奶茶怎么加冰了?");System.out.println("服务员:对不起,珍珠奶茶做错了,重新给您做。");mango.addDosing();System.out.println("不加冰的珍珠奶茶制作完成");System.out.println("我:好的,谢谢!");}
}

输出结果

服务员:你好,需要点什么呀?
我: 一杯加芒果、加珍珠的珍珠奶茶,一杯加柠檬、加冰的柠檬水
服务员:好的。
开始制作:珍珠奶茶
制作中:加珍珠
制作中:加芒果
制作中:加冰
第一杯制作完成
开始制作:柠檬水
制作中:加柠檬
制作中:加冰
第二杯制作完成
我:珍珠奶茶怎么加冰了?
服务员:对不起,珍珠奶茶做错了,重新给您做。
开始制作:珍珠奶茶
制作中:加珍珠
制作中:加芒果
不加冰的珍珠奶茶制作完成
我:好的,谢谢!

到此,女朋友喝到了秋天的第一杯奶茶。

应用场景

  • 动态的增加对象的功能;
  • 不能以派生子类的方式来扩展功能;
  • 限制对象的执行条件;
  • 参数控制和检查等;

相关文章:

设计模式——装饰器模式

装饰器模式 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。 装饰器模式通过将对象包装在装饰器类中,以便动态…...

①matlab的命令掌握

目录 输入命令 命名变量 保存和加载变量 使用内置的函数和常量 输入命令 1.您可以通过在命令行窗口中 MATLAB 提示符 (>>) 后输入命令 任务 使用命令 3*5 将数值 3 和 5 相乘。 答案 3*5 2.除非另有指定,否则 MATLAB 会将计算结果存储在一个名为 ans…...

MySQL----索引

一、索引的概念 索引是一个排序的列表,在这个列表中存储着索引的值和包含这个值的数据所在行的物理地址(类似于c语言的链表通过指针指向数据记录的内存地址)。使用索引后可以不用扫描全表来定位某行的数据,而是先通过索引表找到该…...

秒杀系统的业务流程以及优化方案(实现异步秒杀)

先看基本的业务流程 那么我们可以看到整个流程都是一个线程来完成的,这样的话耗时还是很长的,那么可不可以采用多线程去实现呢? 首先我们要思考怎么对业务进行拆分,可以想象一个我们去饭店点餐,会有前台接待&#xff…...

Java实现根据商品ID获取1688商品详情跨境属性数据,1688商品重量数据接口,1688API接口封装方法

要通过1688的API获取商品详情跨境属性数据,您可以使用1688开放平台提供的接口来实现。以下是一种使用Java编程语言实现的示例,展示如何通过1688开放平台API获取商品详情属性数据接口: 首先,确保您已注册成为1688开放平台的开发者…...

前端面试的性能优化部分(14)每天10个小知识点

目录 系列文章目录前端面试的性能优化部分(1)每天10个小知识点前端面试的性能优化部分(2)每天10个小知识点前端面试的性能优化部分(3)每天10个小知识点前端面试的性能优化部分(4)每天…...

Uniapp笔记(六)uniapp基础

一、腾讯地图 1、uniapp地图渲染 <template><view><map class"map" :longitude"longitude" :latitude"latitude"></map></view> </template> <script>export default {data() {return {longitude:1…...

C++ sort函数用法

sort函数是C标准库中的一个排序算法&#xff0c;头文件是algorithm&#xff0c;用于对容器中的元素进行排序。它可以对任何可排序的容器&#xff08;如数组、向量、列表等&#xff09;进行排序。 有以下四个基本用法&#xff1a; 1. 自定义排序规则&#xff1a;可以通过提供自…...

电子仓库预测水浸事件,他怎么做到的?

仓库环境中水浸事件可能导致严重的损失&#xff0c;不仅对货物造成损害&#xff0c;还可能影响设备的正常运行甚至威胁安全。 因此&#xff0c;为了应对这一挑战&#xff0c;引入一套完善的仓库水浸监控系统成为了不可或缺的措施。 客户案例 广东某电子公司是一家领先的电子设…...

CMake调用第三方库的两种方法

为了让连接器搜索到库路径&#xff0c;一般有两种方法 link_directories命令 使用步骤## 在add_executable或add_library前引入第三方库 # 1.引入第三方库,${THIRD_PARTY_PREFIX}为用户定义的第三方库目录 link_directories(${THIRD_PARTY_PREFIX}/lib) # 2.增加第三方库头文…...

Django基础7——用户认证系统、Session管理、CSRF安全防护机制

文章目录 一、用户认证系统二、案例&#xff1a;登陆认证2.1 平台登入2.2 平台登出2.3 login_required装饰器 三、Django Session管理3.1 Django使用Session3.1.1 Cookie用法3.1.2 Session用法 3.2 案例&#xff1a;用户登录认证 四、Django CSRF安全防护机制 一、用户认证系统…...

基于流计算 Oceanus(Flink) CDC 做好数据集成场景

由于第一次做实时&#xff0c;所以踩坑比较多&#xff0c;见谅(测试环境用的flink),小公司没有用到hadoop组件 一、踩坑记录 1:本地代码的flink版本是flink1.15.4&#xff0c;生产环境是flink1.16.1&#xff0c;在使用侧输出流时报错&#xff0c;需要使用以下写法,需要使用Si…...

MySQL8.Xx安装控制台未生成随机密码解决方案

MySQL8.xx一主两从复制安装与配置 MySQL8.XX随未生成随机密码解决方案 MySQL8.0.30一主两从复制与配置(一) 一: Mysql 安装时控制台未生成密码 安装过程中解压或者安装时报错等,这种情况一般是因网络等其他原因导致下载的安装包不完整&#xff0c; 重新下载安装即可; 二:…...

安装VS2005时提示:请插入磁盘:visual studio 2005 DVD

安装VS2005时提示&#xff1a;请插入磁盘:visual studio 2005 DVD 修改卷标为 "DVD1"...

OpenVINO2023使用简介

1 下载安装 先在anaconda中创建一个虚拟环境&#xff0c;该环境的python版本为3.7&#xff0c;之所以使用python3.7&#xff0c;是因为我在3.9上安装过程中出现不少bug&#xff0c;后面新建了一个3.7的环境才解决&#xff0c;我不知道是否由于和我已有环境中某些包不兼容&…...

基于React实现无限滚动的日历详细教程,附源码【手写日历教程第二篇】

前言 最常见的日历大部分都是滚动去加载更多的月份&#xff0c;而不是让用户手动点击按钮切换日历月份。滚动加载的交互方式对于用户而言是更加丝滑和舒适的&#xff0c;没有明显的操作割裂感。 那么现在需要做一个这样的无限滚动的日历&#xff0c;前端开发者应该如何去思考…...

68、使用aws官方的demo和配置aws服务,进行视频流上传播放

基本思想:参考官方视频,进行了配置aws,测试了视频推流,rtsp和mp4格式的视频貌似有问题,待调研和解决 第一步:1) 进入aws的网站,然后进入ioT Core 2)先配置 Thing types & Thing,选择香港的节点,然后AWS ioT--->Manage---> Thing type 然后输入名字,创建Th…...

数据库

表 记录&#xff1a;行 字段&#xff08;属性&#xff09;: 列 以行列的形式就组成了表&#xff08;数据存储在表中&#xff09; 关系数据库的表由记录组成&#xff0c;记录由字段组成&#xff0c;字段由字符或数字组成。它可以供各种用户共享&#xff0c; 具有最小冗余度和较高…...

深入了解fcntl函数:Linux系统编程中的文件控制

文章目录 概述介绍函数原型与参数 拓展&#xff1a;fcntl改文件属性总结 概述 摘要: fcntl函数是Linux系统编程中一个重要的函数&#xff0c;用于对文件描述符进行各种控制操作。本文将详细介绍fcntl函数的原型、各个参数的用法&#xff0c;以及阻塞和非阻塞模式切换的方法&am…...

汇川技术内推码

[庆祝]不一样的内推码[庆祝]&#xff1a;IVSM2R 投递了可以评论下名字&#xff0c;我会帮忙留意进度。 汇尔成川&#xff0c;共赴星海&#xff0c;欢迎加入&#xff0c;职等你来。 嵌入式软硬件&#xff0c;机器人算法&#xff0c;电机控制&#xff0c;通信软件&#xff0c;PLC…...

网络编程(Modbus进阶)

思维导图 Modbus RTU&#xff08;先学一点理论&#xff09; 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议&#xff0c;由 Modicon 公司&#xff08;现施耐德电气&#xff09;于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...

Linux 文件类型,目录与路径,文件与目录管理

文件类型 后面的字符表示文件类型标志 普通文件&#xff1a;-&#xff08;纯文本文件&#xff0c;二进制文件&#xff0c;数据格式文件&#xff09; 如文本文件、图片、程序文件等。 目录文件&#xff1a;d&#xff08;directory&#xff09; 用来存放其他文件或子目录。 设备…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂

蛋白质结合剂&#xff08;如抗体、抑制肽&#xff09;在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上&#xff0c;高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术&#xff0c;但这类方法普遍面临资源消耗巨大、研发周期冗长…...

无法与IP建立连接,未能下载VSCode服务器

如题&#xff0c;在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈&#xff0c;发现是VSCode版本自动更新惹的祸&#xff01;&#xff01;&#xff01; 在VSCode的帮助->关于这里发现前几天VSCode自动更新了&#xff0c;我的版本号变成了1.100.3 才导致了远程连接出…...

STM32F4基本定时器使用和原理详解

STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...

【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力

引言&#xff1a; 在人工智能快速发展的浪潮中&#xff0c;快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型&#xff08;LLM&#xff09;。该模型代表着该领域的重大突破&#xff0c;通过独特方式融合思考与非思考…...

苍穹外卖--缓存菜品

1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得&#xff0c;如果用户端访问量比较大&#xff0c;数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据&#xff0c;减少数据库查询操作。 缓存逻辑分析&#xff1a; ①每个分类下的菜品保持一份缓存数据…...

Linux-07 ubuntu 的 chrome 启动不了

文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了&#xff0c;报错如下四、启动不了&#xff0c;解决如下 总结 问题原因 在应用中可以看到chrome&#xff0c;但是打不开(说明&#xff1a;原来的ubuntu系统出问题了&#xff0c;这个是备用的硬盘&a…...

MySQL 8.0 OCP 英文题库解析(十三)

Oracle 为庆祝 MySQL 30 周年&#xff0c;截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始&#xff0c;将英文题库免费公布出来&#xff0c;并进行解析&#xff0c;帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...

【JavaWeb】Docker项目部署

引言 之前学习了Linux操作系统的常见命令&#xff0c;在Linux上安装软件&#xff0c;以及如何在Linux上部署一个单体项目&#xff0c;大多数同学都会有相同的感受&#xff0c;那就是麻烦。 核心体现在三点&#xff1a; 命令太多了&#xff0c;记不住 软件安装包名字复杂&…...