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

JAVA设计模式-装饰者模式

一.概念

装饰器模式(Decorator Pattern),动态地给一个对象添加一些额外的职责,就增加功能来说,装饰器模式比生成子类更灵活。 —-《大话设计模式》

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

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

动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。

二.角色

  • Component(抽象构件):给出一个抽象接口,装饰器模式中公共方法的类,在装饰器模式结构图的顶层,以规范准备接收附加责任的对象。
  • ConcreteComponent(具体构件):是要动态扩展的对象,转换器模式中具体的被装饰的类,它继承自Component。
  • Decorator(装饰器):持有一个构件(Component)对象的实例,它是装饰器模式中的核心对象,所有具体装饰器对象的父类,完成装饰器的部分职能。可以只对被装饰的对象进行一些简单的包裹,也可包含对Component中方法的实现。
  • ConcreteDecorator(具体装饰):完成具体的装饰功能。装饰功能的实现是通过调用被装饰对象对应的方法,加上装饰对象自身的方法。这是装饰器模式动机中的添加额外功能的关键。

三.优缺点

1.优点
  • 装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。
  • 装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。装饰模式允许系统动态决定“贴上”或者除掉一个“装饰”,继承关系是静态的,它在系统运行前就决定了;
  • 通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合;
  • 装饰者类可以在被装饰者的行为前面或后面加上自己的行为,甚至取代被装饰者的行为,达到特定的目的;
  • 装饰者一般对组件的客户是透明的,除非客户程序依赖于组件的具体类型
2.缺点
  • 多层装饰比较复杂。
  • 由于使用装饰模式,可以比使用继承关系需要较少数目的类。使用较少的类,当然使设计比较易于进行。但是,在另一方面,使用装饰模式会产生比使用继承关系更多的对象。更多的对象会使得查错变得困难,特别是这些对象看上去都很相像。

四.适用场景

  • 运行时,你需要动态地为对象增加额外职责时;
  • 当你需要一个能够代替子类的类,借助它提供额外方法时。
  • 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责;
  • 处理那些可以撤销的职责;
  • 当不能采用生成子类的方式进行扩充时。

五.装饰器模式与适配器模式的比较

共同点:都拥有一个目标对象。装饰器通过包装一个装饰对象来扩展其功能,而又不改变其接口,这实际上是基于对象的适配器模式的一种变种。
不同点:适配器模式需要实现另外一个接口,而装饰器模式必须实现该对象的接口。适配器模式主要是为了接口的转换,而装饰者模式关注的是通过组合来动态的为被装饰者注入新的功能或行为(即所谓的责任)。

适配器模式详解传送门:JAVA设计模式-适配器模式_Mr Tang的博客-CSDN博客

六.实现

假设我去买咖啡,首先服务员给我冲了一杯原味咖啡,我希望服务员给我加些牛奶和白糖混合入原味咖啡中。使用装饰器模式就可以解决这个问题。

咖啡接口

定义了获取花费和配料的接口。

/*** 咖啡接口*/
public interface Coffee {/*** 获取价格* @return*/public float getPrice();/*** 获取咖啡* @return*/public String getCoffee();
}
原味咖啡

实现Coffe接口,花费1元,配料中,只有咖啡

/*** 原味咖啡类*/
public class OriginalCoffee implements Coffee {@Overridepublic float getPrice() {return 1;}@Overridepublic String getCoffee() {return "原味咖啡";}
}
装饰器类

咖啡对象的装饰器类,同样实现Coffee接口,定义一个Coffe对象的引用,在构造器中进行初始化。并且将getPrice()和getCoffee()方法转发给被装饰对象。

/*** 咖啡的"装饰器",可以给咖啡添加各种"配料"* 该类是一个抽象类需要具体子类来实现*/
public class DecoratorAbstractCoffee implements Coffee {/*** 具体咖啡的接口*/protected final  Coffee coffee;/*** 构造方法,初始化咖啡对象的引用* @param coffee*/public DecoratorAbstractCoffee(Coffee coffee) {this.coffee = coffee;}/*** 获取价格,装饰器父类中直接转发"请求"至引用对象* @return*/@Overridepublic float getPrice() {return coffee.getPrice();}/*** 获取咖啡,装饰器父类中直接转发"请求"至引用对象* @return*/@Overridepublic String getCoffee() {return coffee.getCoffee();}
}
具体的装饰器类

具体的装饰器类,负责往咖啡中“添加”牛奶,注意看getPrice()方法和getCoffee()方法,可以在转发请求之前或者之后,增加功能。如果是代理模式,这里的结构就有所不同,通常代理模式根据运行时的条件来判断是否转发请求。

/*** 混合牛奶到蜂蜜中*/
public class CreamCoffee extends DecoratorAbstractCoffee {private float price = (float) 0.5;/*** 调用父类的构造方法* @param coffee*/public CreamCoffee(Coffee coffee) {super(coffee);}/*** 增加配料需要加钱* @return*/@Overridepublic float getPrice() {return coffee.getPrice()+price;}/*** 对咖啡进行加工* @return*/@Overridepublic String getCoffee() {return coffee.getCoffee()+";添加牛奶";}
}
添加糖

另一个具体装饰器类,用来给咖啡加蜂蜜,一样的逻辑。

class HoneyCoffee extends DecoratorAbstractCoffee {private float price = (float) 1.4;public HoneyCoffee(Coffee coffee) {super(coffee);}@Overridepublic float getPrice() {return coffee.getPrice()+price;}@Overridepublic String getCoffee() {return coffee.getCoffee()+";添加蜂蜜";}
}
客户端使用
public class DecoratorMain {public static void main(String[] args) {//是不是很像 javaIO中的 stream流Coffee coffee = new CreamCoffee(new HoneyCoffee(new OriginalCoffee()));System.out.println(coffee.getCoffee());System.out.println(coffee.getPrice());}
}

七.装饰者模式在Spring Boot中的应用

装饰者模式在Spring Boot中有着广泛的应用,主要用于动态地扩展和改变对象的行为和状态。以下是装饰者模式在Spring Boot中的一些常见应用:

  1. 自定义异常处理:Spring Boot允许你通过装饰者模式来为你的控制器添加自定义的异常处理。你可以创建一个异常处理器,然后通过装饰者模式将其添加到Spring MVC的异常处理器链中。这样,当控制器抛出异常时,你的自定义处理器就会被调用。
  2. 安全性控制:Spring Security是Spring Boot中用于安全性控制的一个库。它使用了装饰者模式来动态地为请求添加安全性检查。你可以创建一个自定义的安全性过滤器,然后将其添加到Spring Security的过滤器链中,以实现自定义的安全性控制。
  3. 响应式编程:Spring WebFlux是Spring Boot中的响应式编程框架。它使用了装饰者模式来动态地为服务器端点添加响应式处理。你可以创建一个自定义的响应式处理器,然后将其添加到WebFlux的处理器链中,以实现自定义的响应式处理逻辑。
  4. 拦截器:Spring MVC允许你使用拦截器来在控制器处理请求之前或之后执行一些操作。拦截器实际上就是使用了装饰者模式。你可以创建一个自定义的拦截器,然后将其添加到Spring MVC的拦截器链中,以实现自定义的请求拦截和处理。

这些应用只是装饰者模式在Spring Boot中的一部分。事实上,只要你需要动态地扩展和改变对象的行为和状态,装饰者模式就可能是一个好的解决方案。

八.总结

​ 装饰器模式是代替增加子类的一种解决方案,体现了聚合/合成复用原则的思想,尽量使用组合的方式来扩展功能,这样就把基本功能和扩展功能解耦了,使得代码可复用,可维护,灵活。关键点在于装饰器模式可以动态地为对象增加扩展功能。

相关文章:

JAVA设计模式-装饰者模式

一.概念 装饰器模式(Decorator Pattern),动态地给一个对象添加一些额外的职责,就增加功能来说,装饰器模式比生成子类更灵活。 —-《大话设计模式》 允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属…...

STM32F1简介

前言 本次学习使用的是STM32F1系列的芯片,72MHz的Cortex-M3内核; 名词解释 STM32是ST公司基于ARM Cortex-M内核开发的32位微控制器(MCU); ARM Cortex-M内核是ARM公司设计的,程序指令的执行,…...

SpringBoot面试题6:Spring Boot 2.X 有什么新特性?与 1.X 有什么区别?

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:Spring Boot 2.X 有什么新特性?与 1.X 有什么区别? Spring Boot是一种用于简化Spring应用程序开发的框架,它提供了自动配置、起步依赖和快速开…...

qt笔记之qml下拉标签组合框增加发送按钮发送标签内容

qt笔记之qml下拉标签组合框增加发送按钮发送标签内容 code review! 文章目录 qt笔记之qml下拉标签组合框增加发送按钮发送标签内容1.运行2.文件结构3.main.qml4.main.cc5.MyClass.h6.MyClass.cc7.CMakeLists.txt8.ComboBox.pro9.qml.qrc 1.运行 2.文件结构 3.main.qml 代码 …...

linux上构建任意版本的rocketmq多架构x86 arm镜像——筑梦之路

现状 目前市面上和官方均只有rocketmq x86架构下的docker镜像,而随着国产化和信创适配的需求越来越多,显然现有的x86架构下的docker镜像不能满足多样化的需求,因此我们需要根据官方发布的版本制作满足需求的多架构镜像,以在不同cp…...

Java8 新特性之Stream(五)-- Stream的3种创建方法

目录 1. 集合 创建Stream流 拓展: 2. 数组 创建Stream流 3. 静态方法 创建Stream流 1. 集合 创建Stream流 @...

Vue实现模糊查询搜索功能

第一步 先创建一个val变量 // 用户搜索内容 let val ref(""); 第二步&#xff1a;给input绑定v-model &#xff08;为了获取input框的值&#xff09; <input v-model"val" type"text" placeholder"请输入行业/公司/名称"/> 第…...

(C++ STL) 详解vector模拟实现

目录 一.vector的介绍 1.vector的介绍 二.vector的定义模拟实现 三.vector各接口的模拟实现 1.vector迭代器的模拟实现 2.构造函数 2.1无参构造 2.2 n个val构造 2.3迭代器区间构造 2.4通过对象初始化&#xff08;拷贝构造&#xff09; 3.析构函数 4.size 5.operato…...

c语言从入门到实战——C语言数据类型和变量

C语言数据类型和变量 前言1. 数据类型介绍1.1 字符型1.2 整型1.3 浮点型1.4 布尔类型1.5 各种数据类型的长度1.5.1 sizeof操作符1.5.2 数据类型长度1.5.3 sizeof中表达式不计算 2. signed 和 unsigned3. 数据类型的取值范围4. 变量4.1 变量的创建4.2 变量的分类 5. 算术操作符&…...

[论文精读]Semi-Supervised Classification with Graph Convolutional Networks

论文原文&#xff1a;[1609.02907] Semi-Supervised Classification with Graph Convolutional Networks (arxiv.org) 论文代码&#xff1a;GitHub - tkipf/gcn: Implementation of Graph Convolutional Networks in TensorFlow 英文是纯手打的&#xff01;论文原文的summari…...

CICD:使用docker+ jenkins + gitlab搭建cicd服务

持续集成解决什么问题 提高软件质量效率迭代便捷部署快速交付、便于管理 持续集成&#xff08;CI&#xff09; 集成&#xff0c;就是一些孤立的事物或元素通过某种方式集中在一起&#xff0c;产生联系&#xff0c;从而构建一个有机整体的过程。 持续&#xff0c;就是指长期…...

新能源电池试验中准确模拟高空环境大气压力的解决方案

摘要&#xff1a;针对目前新能源电池热失控和特性研究以及生产中缺乏变环境压力准确模拟装置、错误控制方法造成环境压力控制极不稳定以及氢燃料电池中氢气所带来的易燃易爆问题&#xff0c;本文提出了相应的解决方案。方案的关键一是采用了低漏率电控针阀作为下游控制调节阀实…...

Python 中的模糊字符串匹配

文章目录 Python中使用thefuzz模块匹配模糊字符串使用process模块高效地使用模糊字符串匹配今天,我们将学习如何使用 thefuzz 库,它允许我们在 python 中进行模糊字符串匹配。 此外,我们将学习如何使用 process 模块,该模块允许我们借助模糊字符串逻辑有效地匹配或提取字符…...

记录一个奇怪bug

一开始Weapon脚本是继承Monobehavior的&#xff0c;实例化后挂在gameObject上跟着角色。后来改成了不继承mono的&#xff0c;也不实例化。过程都是顺利的&#xff0c;运行也没问题&#xff0c;脚本编辑器也没有错误。 但偶尔有一次报了一些错误&#xff0c;大概是说Weapon (1)…...

SpringBoot面试题7:SpringBoot支持什么前端模板?

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:SpringBoot支持什么前端模板? Spring Boot支持多种前端模板,其中包括以下几种常用的: Thymeleaf:Thymeleaf是一种服务器端Java模板引擎,能够…...

leetcode做题笔记172. 阶乘后的零

给定一个整数 n &#xff0c;返回 n! 结果中尾随零的数量。 提示 n! n * (n - 1) * (n - 2) * ... * 3 * 2 * 1 示例 1&#xff1a; 输入&#xff1a;n 3 输出&#xff1a;0 解释&#xff1a;3! 6 &#xff0c;不含尾随 0示例 2&#xff1a; 输入&#xff1a;n 5 输出&a…...

linux之shell脚本练习

以下脚本已经是在ubuntu下测试的 demo持续更新中。。。 1、for 循环测试&#xff0c;&#xff0c;&#xff0c;Ping 局域网 #!/bin/bashi1 for i in {1..254} do# 每隔0.3s Ping 一次&#xff0c;每次超时时间3s&#xff0c;Ping的结果直接废弃ping-w 3 -i 0.3 192.168.110.$i…...

CSS阶详细解析一

CSS进阶 目标&#xff1a;掌握复合选择器作用和写法&#xff1b;使用background属性添加背景效果 01-复合选择器 定义&#xff1a;由两个或多个基础选择器&#xff0c;通过不同的方式组合而成。 作用&#xff1a;更准确、更高效的选择目标元素&#xff08;标签&#xff09;。…...

osWorkflow-1——osWorkflow官方例子部署启动运行(版本:OSWorkflow-2.8.0)

osWorkflow-1——osWorkflow官方例子部署启动运行&#xff08;版本&#xff1a;OSWorkflow-2.8.0&#xff09; 1. 前言——准备工作1.1 下载相关资料1.2 安装翻译插件 2. 开始搞项目2.1 解压 .zip文件2.2 简单小测&#xff08;war包放入tomcat&#xff09;2.3 导入项目到 IDE、…...

Stm32_标准库_13_串口蓝牙模块_手机与蓝牙模块通信

代码&#xff1a; #include "stm32f10x.h" // Device header #include "Delay.h" #include "OLED.h" #include "Serial.h"char News[100] "";uint8_t flag 1;void Get_Hc05News(char *a){uint32_t i 0…...

内存分配函数malloc kmalloc vmalloc

内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...

Linux链表操作全解析

Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表&#xff1f;1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...

通过Wrangler CLI在worker中创建数据库和表

官方使用文档&#xff1a;Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后&#xff0c;会在本地和远程创建数据库&#xff1a; npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库&#xff1a; 现在&#xff0c;您的Cloudfla…...

【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密

在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...

Module Federation 和 Native Federation 的比较

前言 Module Federation 是 Webpack 5 引入的微前端架构方案&#xff0c;允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...

leetcodeSQL解题:3564. 季节性销售分析

leetcodeSQL解题&#xff1a;3564. 季节性销售分析 题目&#xff1a; 表&#xff1a;sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...

让AI看见世界:MCP协议与服务器的工作原理

让AI看见世界&#xff1a;MCP协议与服务器的工作原理 MCP&#xff08;Model Context Protocol&#xff09;是一种创新的通信协议&#xff0c;旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天&#xff0c;MCP正成为连接AI与现实世界的重要桥梁。…...

AI,如何重构理解、匹配与决策?

AI 时代&#xff0c;我们如何理解消费&#xff1f; 作者&#xff5c;王彬 封面&#xff5c;Unplash 人们通过信息理解世界。 曾几何时&#xff0c;PC 与移动互联网重塑了人们的购物路径&#xff1a;信息变得唾手可得&#xff0c;商品决策变得高度依赖内容。 但 AI 时代的来…...

【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案

目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后&#xff0c;迭代器会失效&#xff0c;因为顺序迭代器在内存中是连续存储的&#xff0c;元素删除后&#xff0c;后续元素会前移。 但一些场景中&#xff0c;我们又需要在执行删除操作…...

前端中slice和splic的区别

1. slice slice 用于从数组中提取一部分元素&#xff0c;返回一个新的数组。 特点&#xff1a; 不修改原数组&#xff1a;slice 不会改变原数组&#xff0c;而是返回一个新的数组。提取数组的部分&#xff1a;slice 会根据指定的开始索引和结束索引提取数组的一部分。不包含…...