JAVA设计模式-装饰者模式

一.概念

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

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

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

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

二.角色

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

三.优缺点

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

四.适用场景

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

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

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

六.实现

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

咖啡接口

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

/*** 咖啡接口*/
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中的一部分。事实上,只要你需要动态地扩展和改变对象的行为和状态,装饰者模式就可能是一个好的解决方案。

八.总结

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

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

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

相关文章

银河麒麟安装arm架构mysql8

1. 准备工作 2. 查看麒麟系统版本 使用命令 Linux version 4.19.90-25.21.v2101.ky10.aarch64 (KYLINSOFTlocalhost.localdomain) (gcc version 7.3.0 (GCC)) #1 SMP Wed Sep 28 16:37:42 CST 2022可以看出这是麒麟 v10 ,aarch64 (ARM 架构的&#xff…

排序算法-冒泡排序法(BubbleSort)

排序算法-冒泡排序法(BubbleSort) 1、说明 冒泡排序法又称为交换排序法,是从观察水中的气泡变化构思而成的,原理是从第一个元素开始,比较相邻元素的大小,若大小顺序有误,则对调后再进行下一个…

Java实现B树

1.介绍 B树是一种自平衡的搜索树数据结构,常用于数据库和文件系统中的索引结构。它具有以下好处和功能: 高效的查找操作:B树的特点是每个节点可以存储多个关键字,并且保持有序。通过在节点上进行二分查找,可以快速定位…

Ubuntu22.04.3安装教程

虚拟机系列文章 VMware Workstation Player 17 免费下载安装教程 VMware Workstation 17 Pro 免费下载安装教程 windows server 2012安装教程 Ubuntu22.04.3安装教程 FTP服务器搭建 Ubuntu22.04.3安装教程 虚拟机系列文章前言Ubuntu22.04.3安装(图文) 前…

2ED2410-EM:12v / 24v智能模拟高侧MOSFET栅极驱动器

概述 12v / 24v智能模拟高侧MOSFET栅极驱动器。 特性 PRO-SIL ISO 26262-准备根据ISO 26262:2018条款8-13支持硬件元件评估的集成商。一个通道器件具有两个高侧栅极驱动器输出。3 Ω下拉,50 Ω上拉,用于快速开关开/关。支持背靠背MOSFET拓扑(共漏极和共源)。两个双向高侧模拟…

STM32:GPIO模拟SPI驱动ADS8361

ADS8361是TI公司开发的一款模拟量输入芯片。ADS8361有四种工作模式,本文主要针对模式三进行通信驱动。官方方案使用两路SPI来通信,一路SPI Master,一路SPI Slave。我在使用STM32主控芯片的两路SPI进行通信的时候,发现只有SPI Mast…

swift ui 布局 ——Stack(HStack、VStack、ZStack)

一、HStack 水平布局 将其子视图排列在水平线上 import Foundation import SwiftUI struct MyView: View {var body: some View {HStack{Text("text")Image("yuyin").resizable().frame(width: 102,height: 80)}} } 默认子视图是水平中心对齐的,可添加al…

软件测试学习(一)基础概念、实质、说明书测试、分类、动态黑盒测试

软件测试概念、背景 软件无处不在。然而,软件是人写的一所以不完美。 世界上有完美的软件吗?NO 世界上没有完美的软件。所有软件都可能存在缺陷、错误或漏洞,无论是操作系统、应用程序、游戏还是其他类型的软件。这些问题可能会导致功能问题…

【高等数学】极限(上)(最全万字详解)

文章目录 1、数列的极限1.1、数列极限的定义1.2、为什么收敛数列极限是唯一的?1.3、为什么收敛数列是有界的?1.4、数列极限的保号性1.4.1、极限保数列值1.4.2、数列值保极限值 1.5、收敛数列与其子列之间的关系 2、函数极限概念2.1、函数极限的定义2.1.1…

tomcat服务安装步骤以及详细配置教程

tomcat服务安装步骤以及详细配置教程 文章目录 tomcat服务安装步骤以及详细配置教程1.简介2.优缺点优点:缺点: 3.工作原理4.工作流程5.实战(tomcat项目部署)5.1.java环境安装5.2.拉取tomcat软件包5.3.解压部署5.4.启动tomcat服务5…

conda: error: argument COMMAND: invalid choice: ‘activate‘

参考:https://github.com/conda/conda/issues/13022 输入后重启terminal即可

git push错误->Error: src refspec master does not match any

参考:https://blog.csdn.net/weixin_40908748/article/details/128574907 问题描述:在执行命令 git push origin master 时报错->Error: src refspec master does not match any 问题分析:在网上查找解决方法,大部分人说是暂存区没有文件…

zsh: command not found: conda问题解决

参考:https://zhuanlan.zhihu.com/p/158703094 一、问题介绍与环境介绍 系统为macOS Catalina 10.15.4 所用终端为zsh 安装了oh-my-zsh之后conda命令在终端中不可用。 二、原因分析 终端中zsh的可访问的程序一般放在/bin, /usr/bin, /usr/local/bin,/bin目录下&…

阿里云上了新闻联播

我是卢松松,点点上面的头像,欢迎关注我哦! 阿里新任的CEO吴泳铭上央视新闻联播了! 在昨天的新闻联播里,出席科技座谈会,有一个特别镜头,出现了阿里新任CEO吴泳铭的镜头。 这个信号意义明显,我…

L14D6内核模块编译方法

一、内核模块基础代码解析 一个内核模块代码错误仍然会导致的内核崩溃。 GPL协议:开源规定,使用内核一些函数需要 1、单内核的缺点 单内核扩展性差的缺点减小内核镜像文件体积,一定程度上节省内存资源提高开发效率不能彻底解决稳定性低的缺…

如何在 Keras 中开发具有注意力的编码器-解码器模型

link 【翻译自 : How to Develop an Encoder-Decoder Model with Attention in Keras 】 【说明:Jason Brownlee PhD大神的文章个人很喜欢,所以闲暇时间里会做一点翻译和学习实践的工作,这里是相应工作的实践记录,…

Vue-1.9工程化开发和脚手架

开发Vue的两种方式: 1.核心包传统开发模式:基于html/css/js文件,直接引入核心包,开发Vue 2.工程化开发模式:基于构建工具(例如:webpack)的环境中开发Vue 问题: 1&…

排序算法-选择排序法(SelectionSort)

排序算法-选择排序法(SelectionSort) 1、说明 选择排序法也是枚举法的应用,就是反复从未排序的数列中取出最小的元素,加入另一个数列中,最后的结果即为已排序的数列。选择排序法可使用两种方式排序,即在所…

XML是不是主要用做配置文件?

2023年10月11日,周三下午 这几天发现tomcat的配置文件主要是用XML文件来写的, 于是就有了这个问题。 是的,XML非常适合用来做配置文件。 XML作为配置文件的主要优点: 可读性强。XML使用标签结构组织数据,内容清晰易懂。跨语言和跨平台。XML作为纯文本…

Fisher辨别分析

问题要求 在UCI数据集上的Iris和Sonar数据上验证算法的有效性。训练和测试样本有三种方式(三选一)进行划分: (一) 将数据随机分训练和测试,多次平均求结果 (二)K折交叉验证 &…