设计模式之装饰模式--优雅的增强
目录
- 概述
- 什么是装饰模式
- 为什么使用装饰模式
- 关键角色
- 基本代码
- 应用场景
- 版本迭代
- 版本一
- 版本二
- 版本三—装饰模式
- 装饰模式中的巧妙之处
- 1、被装饰对象和装饰对象共享相同的接口或父类
- 2、当调用装饰器类的装饰方法时,会先调用被装饰对象的同名方法
- 3、子类方法与父类方法共享相同的引用
- 4、装饰模式与继承的对比
- 总结
概述
什么是装饰模式
装饰模式(Decorator Pattern)是一种结构型设计模式,它允许通过将对象放入包含行为的特殊封装对象中来为原始对象添加新的行为。装饰模式在不改变原始对象接口的情况下,动态地将责任附加到对象上。就增加功能来看,装饰模式比生成子类更为灵活。
为什么使用装饰模式
使用装饰模式有以下几个优点:
灵活性:装饰模式允许在运行时动态地给对象添加功能,而不需要修改其代码或使用继承。这使得系统更加灵活,易于扩展和修改。
单一职责原则:装饰模式能够将不同的功能划分到不同的类中,使每个类只负责单一的功能。这遵循了单一职责原则,使得代码更加清晰、可读性更高。
可组合性:由于装饰模式使用了对象组合而不是继承,因此可以通过不同的方式将装饰器组合起来,以获得不同的行为组合。
装饰模式的结构
关键角色
- 抽象组件(Component):定义了原始对象和装饰器的公共接口,可以是接口或抽象类。
- 具体组件(Concrete Component):实现了抽象组件接口,是被装饰的原始对象。
- 装饰器(Decorator):实现了抽象组件接口,并持有一个抽象组件对象的引用,在装饰器中可以添加一些额外的行为。
-具体装饰器(Concrete Decorator):继承自装饰器,具体实现了在装饰器中定义的额外行为。
基本代码
应用场景
1、在不改变现有对象结构的情况下,对对象的功能进行扩展或修改。
2、需要动态地给对象添加功能,以便根据需要增加或移除功能。
3、对象的职责应该能够被多次扩展,而不会导致类的数量急剧增加。
版本迭代
需求:给人搭配不同的服饰
版本一
//Person
public class Person {private String name;public Person(String name ){this.name=name;}public void wearTShirts(){System.out.println("大T恤");}public void wearBigTrouser(){System.out.println("跨裤");}public void wearSneakers(){System.out.println("球鞋");}public void show(){System.out.println("装扮的"+name);}
}
//客户端
public class Client {public static void main(String[] args) {Person xc=new Person("小蔡");System.out.println("第一种装扮");xc.wearTShirts();xc.wearBigTrouser();xc.wearSneakers();xc.show();System.out.println("--------------");System.out.println("第二种装扮");xc.wearTShirts();xc.wearSneakers();xc.show();}
}
版本一非常简单,给人穿衣服就只有Person这个类,但是需求如果需要增加“超人”的装扮,就需要修改Person类(毕竟超人不是人,不能使用实例化Person,并且超人的衣服最起码是有个内裤和斗篷的~~)
这个时候怎么办,把服饰拿出去作为单独的类是不是就可以了呢
版本二
//Person
public class Person {private String name;public Person(String name){this.name=name;}public void show(){System.out.println("装扮的"+name);}
}//抽象服饰类
public abstract class Finery {public abstract void show();
}//具体的服饰类
public class BigTrouser extends Finery{@Overridepublic void show() {System.out.println("跨裤");}
}public class TShirts extends Finery{@Overridepublic void show() {System.out.println("大t恤");}
}//客户端
public class client {public static void main(String[] args) {Person xc=new Person("小蔡");Finery tt=new TShirts();Finery kk=new BigTrouser();kk.show();tt.show();xc.show();}
}
新的问题好像又来了,客户端看起来就是光着身子的小蔡一件件的穿上了衣服,也就是说穿衣服的过程不应该在客户端显示,而应该在内部完成,并且每个人的爱好不一样,穿衣服的顺序并不确定,这个建造者就不同了,而是需要把所有的功能按照正确顺序串联起来进行控制。装饰模式上场了
版本三—装饰模式
//服饰抽象类
public interface ICharacter {public void show();
}//被装饰对象人
public class Person implements ICharacter{private String name;public Person(String name){this.name=name;}@Overridepublic void show() {System.out.println("装扮的"+name);}
}
//装饰类
public class Finery implements ICharacter{protected ICharacter component;public void decorate(ICharacter component){this.component=component;}@Overridepublic void show() {if(component !=null){component.show();}}
}//具体装饰类
public class Sneakers extends Finery{public void show(){System.out.println("球鞋");super.show();}}public class BigTrouser extends Finery{public void show(){System.out.println("裤子");super.show();}}public class Tshirts extends Finery{public void show(){System.out.println("T恤");super.show();}
}//客户端
public class Client {public static void main(String[] args) {Person xc=new Person("小蔡");System.out.println("第一种装扮");Sneakers pqx=new Sneakers();pqx.decorate(xc);BigTrouser kk=new BigTrouser();kk.decorate(pqx);Tshirts tt=new Tshirts();tt.decorate(kk);tt.show();}
}
装饰模式中的巧妙之处
1、被装饰对象和装饰对象共享相同的接口或父类
这样做的目的是是为了让它们可以互相替代。这种互相替代的能力是装饰模式的关键特点之一。通过共享相同的接口或父类,可以在不修改原始对象的情况下,动态地添加、删除或更改对象的行为。
互相替换的好处在于灵活性和扩展性。当需要为对象添加额外的功能时,可以直接用装饰对象替换原始对象,而不需要修改原始对象的代码。可以将装饰器对象看作是被装饰对象的 “包装”。这样可以避免引入大量的条件语句或继承关系,使得代码更加清晰、可维护和可扩展。
另外,互相替换还可以实现功能的组合和嵌套,比如上述给人装扮的例子,穿了靴子和裤子,就是把靴子和裤子的功能进行了组合。通过将装饰器对象嵌套在其他装饰器对象中,可以构建出复杂的功能组合,以满足不同的需求。
2、当调用装饰器类的装饰方法时,会先调用被装饰对象的同名方法
例如上面装扮的例子中的show方法.装饰器类(服饰类)通过持有一个被装饰对象(component)的引用来添加额外的功能。当调用装饰器类的 show() 方法时,它会先调用被装饰对象的 show()方法,然后再执行自己的装饰逻辑(如下图所示)。这样的设计可以实现动态地扩展对象的功能,而无需改变原始对象的结构。通过嵌套和组合多个装饰器类灵活地添加、移除和组合各种功能,以满足不同的需求。
3、子类方法与父类方法共享相同的引用
在具体装饰子类中都有super.show方法,这个是调取父类的show方法
那么这样写的巧妙之处在哪呢,在这里是看不到2中说到的“”当调用装饰器类的 show() 方法时,它会先用被装饰对象的 show()方法,然后再执行自己的装饰逻辑。因为这个逻辑写到了父类中
在这里有个疑问,在具体的子类中component为什么肯定是它的上一级被装扮对象呢,比如“穿了裤子的小蔡”接着要穿T恤。这就是因为每次调用装扮对象的decorate方法,传入的都是上一级被装扮对象(如下图所示),同时子类没有重写decorate方法,此时子类方法将与父类方法共享相同的引用,并且它们将具有相同的实现和行为,也就把component传给装备对象的属性,以供后面调用show方法时使用。
4、装饰模式与继承的对比
装饰模式将功能添加到对象上的方式与继承不同。使用继承时,子类会继承父类的行为,而装饰模式可以动态地将额外的行为添加到对象上,而不需要继承。这使得装饰模式更加灵活,因为可以在运行时选择不同的行为组合。
另外,装饰模式遵循了开放-关闭原则,即对扩展开放,对修改关闭。通过组合和委托的方式,可以动态地将功能添加到对象上,而不需要修改现有代码
总结
装饰模式是一种灵活且可扩展的设计模式,它可以动态地给对象添加新的行为。使用装饰模式可以避免类爆炸问题,并能够在运行时选择不同的功能组合。它与继承相比更加灵活,符合单一职责原则,并遵循开放-关闭原则。
相关文章:

设计模式之装饰模式--优雅的增强
目录 概述什么是装饰模式为什么使用装饰模式关键角色基本代码应用场景 版本迭代版本一版本二版本三—装饰模式 装饰模式中的巧妙之处1、被装饰对象和装饰对象共享相同的接口或父类2、当调用装饰器类的装饰方法时,会先调用被装饰对象的同名方法3、子类方法与父类方法…...

前端vue,后端springboot。如何防止未登录的用户直接浏览器输入地址访问
前端,使用Vue框架来实现前端路由拦截: 设置需要登录校验的页面: 登录成功后,去设置LocalStorage里面的IsLogin为true:...

linux安装Chrome跑web自动化
添加 Chrome 源: 打开终端并执行以下命令,将 Google Chrome 的 APT 源添加到系统: bashCopy code wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb 安装 Chrome: 执行以下命令来安装 Chrome&…...

linux环境下编译,安卓平台使用的luajit库
一、下载luajit源码 1、linux下直接下载: a、使用curl下载:https://luajit.org/download/LuaJIT-2.1.0-beta3.tar.gz b、git下载地址;https://github.com/LuaJIT/LuaJIT.git 2、Windows下载好zip文件,下载地址:https…...

indexedDB笔记
indexedDB 该部分内容主要源于https://juejin.cn/post/7026900352968425486 常用场景:大量数据需要缓存在本地重要概念 仓库objectStore:类似于数据库中的表,数据存储媒介索引index:索引作为数据的标志量,可根据索引获…...

系统提示缺少或找不到emp.dll文件的详细解决方案
我今天打开一款《游戏》。然而,在游戏中遇到了一个非常棘手的问题:游戏报错找不到emp.dll,无法继续执行代码。这让我们非常苦恼,因为这个问题严重影响了我们的游戏体验。 在经过一番努力之后,我终于找到了4个解决方法,…...

Python实现自动化网页操作
1 准备 推荐使用Chrome浏览器 1.1 安装selenium程序包 激活虚拟环境,打开新的Terminal,输入以下代码: python -m pip install selenium 如下图所示,表示安装成功,版本为4.7.2 安装成功 关闭虚拟环境,打…...

03 矩阵与线性变换
矩阵与线性变换 线性变换如何用数值描述线性变换特殊的线性变换反过来看总结 这是关于3Blue1Brown "线性代数的本质"的学习笔记。 线性变换 如果一个变换具有以下两个性质,我们就称它是线性的: 一是直线在变换后仍然保持为直线二是原点必须…...

MySQL InnoDB数据存储结构
1. 数据库的存储结构:页 索引结构给我们提供了高效的索引方式,不过索引信息以及数据记录都是保存在文件上的,确切说是存储在页结构中。另一方面,索引是在存储引擎中实现的,MySQL服务器上的存储引擎负责对表中数据的读…...

【数据结构】数组和字符串(十五):字符串匹配2:KMP算法(Knuth-Morris-Pratt)
文章目录 4.3 字符串4.3.1 字符串的定义与存储4.3.2 字符串的基本操作4.3.3 模式匹配算法0. 朴素模式匹配算法1. ADL语言2. KMP算法分析3. 手动求失败函数定义例1例2例3 4. 自动求失败函数(C语言)5. KMP算法(C语言)6. 失败函数答案…...

STM32 PWM可控制电压原理
PWM可控制电压原理 主要通过PWM 输入模式根据控制单位时间内输出的平均电压,以调节电压大小。而PWM输出模式通过调节占空比,控制平均电压大小; 设置TIM为PWM输出模式 第一步:时钟使能: GPIO,TIM; 第二步&a…...

angular、 react、vue框架对比
借鉴:Web前端开发:三大主流框架 (baidu.com) AngularReactVue公司ChromeFaceBook尤雨溪写法有指令、模板的概念比较灵活,没有要求使用特定的架构和模式有指令和模板的概念性能低有虚拟Dom,性能高有虚拟Dome,性能高学习门槛 高&am…...

GNSS常用数据源汇总
本文整理汇总了GNSS数据处理过程中常用的数据源,路径中的占位符具体含义如下: -YYYY-年-YY-年的后两位数-DOY-年积日-MM-月-HH-小时-WWWW-GPS周 一、RINEXO观测值与RINEXN星历小时文件 1、CDDIS:ftp://gdc.cddis.eosdis.nasa.gov/pub/gnss…...

01|LangChain | 从入门到实战-介绍
by:wenwenc9 一、基本知识储备 1、什么是大模型,LLM? 大模型(Large Language Model)是近年来一个很热门的研究方向。 使用大量的数据训练出一个非常大的模型。一般是数十亿到上万亿的参数规模。 这些大模型可以捕捉到非常复杂的语言…...

【小白专用】PHP基本语法 23.11.04
PHP基本语法 PHP是超文本预处理器 由服务器解析执行 可以与 html 进行混编(嵌入) ,PHP是一种弱类型语言 1.1 PHP标记 PHP和其他Web语言一样,都是用一对标记将PHP代码包含起来,以便和HTML代码区分开来。PHP支持4种风格的标记,如表所示。 标…...

路由器基础(七):NAT原理与配置
一、NAT 配置 华为路由器配置NAT 的方式有很多种,考试中可能考到的基本配置方 式主要有EasyIP和通过NAT地址池的方式。图22-7-1是一个典型的通过EasyIP进行NAT的示意图,其中Router出接口GE0/0/1的IP地址为200.100.1.2/24,接口E0/0/1的IP地址为192.168.0.…...

Spring Boot 整合SpringSecurity和JWT和Redis实现统一鉴权认证
📑前言 本文主要讲了Spring Security文章,如果有什么需要改进的地方还请大佬指出⛺️ 🎬作者简介:大家好,我是青衿🥇 ☁️博客首页:CSDN主页放风讲故事 🌄每日一句:努力…...

交换机基础(零):交换机基础配置
一、华为设备视图 常用视图 名称 进入视图 视图功能 用户视图 用户从终端成功登录至设备即进 入用户视图,在屏幕上显示 kHuawei> 用户可以完成查看运行状态和统 计信息等功能。在其他视图下 都可使用return直接返回用户视 图 系统视图 在用户视图下&…...

02 线性组合、张成的空间与基
线性组合、张成的空间与基 基向量缩放向量并相加给定向量张成的空间线性相关与线性无关空间的基 这是关于3Blue1Brown "线性代数的本质"的学习笔记。 基向量 当看到一对描述向量的数时,比如[3,-2]时,把这对数中的每个数(坐标&…...

解析mfc100u.dll文件丢失的修复方法,快速解决mfc100u.dll问题
在计算机使用过程中,我们经常会遇到一些错误提示,其中最常见的就是“缺少某个文件”的错误。最近,我也遇到了一个这样的问题,那就是“mfc100u.dll丢失”。这个问题可能会导致某些应用程序无法正常运行,给我们带来困扰。…...

免费外文文献检索网站,你一定要知道
01. Sci-Hub 网址链接:https://tool.yovisun.com/scihub/ Sci-hub是一个可以无限搜索、查阅和下载大量优质论文的数据库。其优点在于可以免费下载论文文献。 使用方法: 在Sci—hub搜索栏中粘贴所需文献的网址或者DOI,然后点击右侧的open即可…...

大数据毕业设计选题推荐-收视点播数据分析-Hadoop-Spark-Hive
✨作者主页:IT研究室✨ 个人简介:曾从事计算机专业培训教学,擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…...

传智杯-21算法赛初赛B组题目详细解法解析-AB题(C/C++、Python、Java)
🚀 欢迎来到 ACM 算法题库专栏 🚀 在ACM算法题库专栏,热情推崇算法之美,精心整理了各类比赛题目的详细解法,包括但不限于ICPC、CCPC、蓝桥杯、LeetCode周赛、传智杯等等。无论您是刚刚踏入算法领域,还是经验丰富的竞赛选手,这里都是提升技能和知识的理想之地。 ✨ 经典…...

post给后端传递数组和多个参数
这是前端的数据结构 data() {return {loading: false,inputForm: {id: ${gridProject.id},gridName: ,gridId: ,projectName: ,projectId: ,type: },data: [],value: []}}, 其中 gridId 和 type 是单个参数 , value 是个数组,注意 这里data中的value[]不要直接给后…...

音频修复增强软件iZotope RX 10 mac中文特点
iZotope RX 10 mac是一款音频修复和增强软件。 iZotope RX 10 mac主要特点 声音修复:iZotope RX 10可以去除不良噪音、杂音、吱吱声等,使音频变得更加清晰干净。 音频增强:iZotope RX 10支持对音频进行音量调节、均衡器、压缩器、限制器等处…...

【面试】虚拟机栈面试题
目录 一、举例栈溢出的情况二、调整栈大小,能保证不出现溢出吗?三、分配的栈内存越大越好吗?四、垃圾回收是否会涉及到虚拟机栈?五、方法中定义的局部变量是否存在线程安全问题?5.1 说明5.2 代码示例 一、举例栈溢出的…...

白话熵增定律
白话熵增定律 热力学中的熵增定律 熵是指一个系统的混乱程度的度量,是热力学中的一个系统的属性。熵增定律是指一个封闭的系统随着时间的发展,在朝平衡状态发展时,其熵会增加,即其越来越混乱。 对于一个房间,如果经常…...

(论文阅读14/100)End-to-end people detection in crowded scenes
文献阅读笔记 简介 题目 End-to-end people detection in crowded scenes 作者 Russell Stewart, Mykhaylo Andriluka 原文链接 https://arxiv.org/pdf/1506.04878.pdf 关键词 Null 研究问题 当前的人员检测器要么以滑动窗口的方式扫描图像,要么对一组离…...

Go的错误处理
什么是错误? 错误表示程序中发生的任何异常情况。假设我们正在尝试打开一个文件,但该文件在文件系统中不存在。这是一种异常情况,表示为错误。 Go 中的错误是普通的旧值。就像任何其他内置类型(例如 int、float64 等)…...

云原生相关概念
云计算 指托管在外部数据中心并按使用量付费提供给用户的软件基础设施。公司不必为昂贵的服务器付费并进行维护。相反,他们可以使用云提供商提供的按需云原生服务,例如存储、数据库和分析。 云原生 是在 云计算环境 中构建、部署和管理现代应用程序的…...