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

设计模式第二章(装饰器模式)

设计模式第二章(装饰器模式)

​ 装饰器模式(Decorator Pattern) 也称为包装模式(Wrapper Pattern) 是指在不改变原有对象的基础之上,将功能附加到对象上,提供了比继承更有弹性的替代方案(扩展原有对象的功能),属于结构型模式。装饰器模式的核心是功能扩展,使用装饰器模式可以透明且动态地扩展类的功能。

案例一

​ 需求背景,我们现在需要删除一个集合中的元素,但是我们需要记录了那些元素是被删除的,我们想想是不是需要一个 list来记录那些元素是被删除的呢,还需要有 Set的功能,那么就需要一个代理对象。

代码部分

public class HistorySet<E> implements Set<E> {//记录删除的元素List<E> removeList = new ArrayList<>();// 代理对象private final Set<E> delegate;public HistorySet(Set<E> delegate) {this.delegate = delegate;}@Overridepublic int size() {return delegate.size();}@Overridepublic boolean isEmpty() {return delegate.isEmpty();}@Overridepublic boolean contains(Object o) {return delegate.contains(o);}@Overridepublic Iterator<E> iterator() {return delegate.iterator();}@Overridepublic Object[] toArray() {return delegate.toArray();}@Overridepublic <T> T[] toArray(T[] a) {return delegate.toArray(a);}@Overridepublic boolean add(E e) {return delegate.add(e);}@Overridepublic boolean remove(Object o) {if (delegate.remove(o)) {// 记录被删除的元素removeList.add((E)o);return true;}return false;}@Overridepublic boolean containsAll(Collection<?> c) {return delegate.containsAll(c);}@Overridepublic boolean addAll(Collection<? extends E> c) {return delegate.addAll(c);}@Overridepublic boolean retainAll(Collection<?> c) {return delegate.retainAll(c);}@Overridepublic boolean removeAll(Collection<?> c) {return delegate.removeAll(c);}@Overridepublic void clear() {delegate.clear();}@Overridepublic String toString() {return delegate.toString() + ", 删除的元素:"+removeList;}
}

测试部分

    public static void main(String[] args) {Set<String> historySet1 = new HistorySet<>(new HashSet<>());Set<String> historySet = new HistorySet<>(historySet1);historySet.add("1");historySet.add("2");historySet.add("3");historySet.add("4");historySet.add("5");historySet.remove("2");historySet.remove("2");historySet.remove("3");historySet.remove("5");historySet.remove("5");System.out.println(historySet);}

在这里插入图片描述

思考部分

​ 装饰器的本质就是增强,自己的内部有一个需要被代理的对象,实际上调用的都是被代理对象的方法,我们不需要更新额外的更改,需要实现被代理对象的接口。那么就拥有和代理对象一样的功能了。

案例一

我们通过字节流读取一个读取一个pdf,这个文件大小为2.6m的大小,我们看传统的字节流读取需要花费多少时间。

代码部分

public static void main(String[] args) {File file = new File("E:\\技术文档\\springboot\\SpringBoot-1.pdf");long s = Instant.now().toEpochMilli();try (FileInputStream fileInputStream = new FileInputStream(file)){while (true) {int length = fileInputStream.read();if (length == -1) {break;}}System.out.println("耗时:"+(Instant.now().toEpochMilli() - s) + " 毫秒!");} catch (IOException e) {throw new RuntimeException(e);}}

在这里插入图片描述

思考部分

我们看到读取一个字节的文件花费了2.5秒的时间,那么这个耗时的动作在哪里呢。我们打开了read()

看到这个范围是 -1到255 个字节之间,那么也就是说每次只能读一个 255的字节的数据,频繁的读取增加了耗时

在这里插入图片描述

优化代码

我们利用装饰器模式,自己的内部有一个FileInputStream 我们利用现有的功能进行增强,只需要将buffer 扩大是不是就可以实现呢

public class BufferInputStream extends InputStream {private byte[] buffer = new byte[8024];// 读取的位置private int position = -1;//读取的容量private int capacity = -1;private final FileInputStream fileInputStream;public BufferInputStream(FileInputStream fileInputStream) {this.fileInputStream = fileInputStream;}@Overridepublic int read() throws IOException {if (buffCanRead()) {// buff 可以读取return readFromBuffer();}//刷新 bufferrefreshBuffer();if (!buffCanRead()) {return -1;}return readFromBuffer();}private int readFromBuffer() {return buffer[position++] & 0xFF;}private void refreshBuffer() throws IOException {capacity = this.fileInputStream.read(buffer);position = 0;}private boolean buffCanRead() {if (capacity == -1) {return false;}if (position == capacity) {return false;}return true;}

测试部分

public static void main(String[] args) {File file = new File("E:\\技术文档\\springboot\\SpringBoot-1.pdf");long s = Instant.now().toEpochMilli();try (BufferInputStream fileInputStream = new BufferInputStream(new FileInputStream(file))){while (true) {int length = fileInputStream.read();if (length == -1) {break;}}System.out.println("耗时:"+(Instant.now().toEpochMilli() - s) + " 毫秒!");} catch (IOException e) {throw new RuntimeException(e);}}

在这里插入图片描述

实战部分

我们平时使用@ResponseBody 注解后,自动会解析为对象,那么我们能否在对象里面增加一个自己的属性呢。

前言

​ 我们创建一个springboot的工程,在接口上定义自己的注解。最终达到增加一个时间戳的值放进去,那么我们一步一步的debug 进去

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.fashion</groupId><artifactId>decorator_pattern-boot</artifactId><version>0.0.1-SNAPSHOT</version><name>decorator_pattern-boot</name><description>decorator_pattern-boot</description><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><spring-boot.version>3.4.1</spring-boot.version><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><java.version>17</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.30</version></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement></project>

controller部分

    @PostMappingpublic Map<String,Object> origin1(@RequestBody Map<String,Object> json) {return json;}POST http://localhost:8080/api
Content-Type: application/json{"name": "张三","age": 18
}

思考部分

  • 我们标记@RequestBody 注解后,是如何将参数组装起来的呢
  • @RequestBody 在何处被使用了呢
  • 找到 HandlerMethodArgumentResolverComposite 类

@RequestBody 参数解析源码部分

在这里插入图片描述

- com.fashion.decorator_patternboot.controller.DecoratorController#origin- org.springframework.web.method.support.InvocableHandlerMethod#doInvoke- org.springframework.web.method.support.InvocableHandlerMethod#invokeForRequest- org.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues- org.springframework.web.method.support.HandlerMethodArgumentResolverComposite#getArgumentResolver

在这里插入图片描述

在这里插入图片描述

​ 至此我们找到了 HandlerMethodArgumentResolverComposite 所属类,我们看到这个类的结构,是不是就一个装饰器模式的使用,肚子里面有一堆解析器

HandlerMethodArgumentResolver

​ 该类为解析器的顶层入口,里面只有两个方法,一个是否支持,另外一个解释如何解析数据。
在这里插入图片描述

RequestResponseBodyMethodProcessor

我们看到该类继承了 extends AbstractMessageConverterMethodProcessor,里面实际上是实现了HandlerMethodArgumentResolver,那么我们可以得到 supportsParameter以及 resolveArgument 方法。

  • @Overridepublic boolean supportsParameter(MethodParameter parameter) {return parameter.hasParameterAnnotation(RequestBody.class);}
    
  • @Override
    @Nullable
    public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {parameter = parameter.nestedIfOptional();Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());if (binderFactory != null) {String name = Conventions.getVariableNameForParameter(parameter);ResolvableType type = ResolvableType.forMethodParameter(parameter);WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name, type);if (arg != null) {validateIfApplicable(binder, parameter);if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());}}if (mavContainer != null) {mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());}}return adaptArgumentIfNecessary(arg, parameter);
    }
    

增加自定义注解

TimestampBody 注解类,该类用来做一个标记。

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TimestampBody {
}

自定义解析器

public class TimeStampResponseBodyMethodProcessor implements HandlerMethodArgumentResolver {private RequestResponseBodyMethodProcessor processor;@Overridepublic boolean supportsParameter(MethodParameter parameter) {return parameter.hasParameterAnnotation(TimestampBody.class);}@Overridepublic Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {Object retObj = processor.resolveArgument(parameter, mavContainer, webRequest, binderFactory);if (retObj instanceof Map) {((Map) retObj).put("timestamp",System.currentTimeMillis()+"");}return retObj;}}

如何将我们自己的解析器放到解析器组合中

相当于我们需要把 TimeStampResponseBodyMethodProcessor 得类放到 HandlerMethodArgumentResolverComposite 的 argumentResolvers 中。

debug 断点

在这里插入图片描述

  1. org.springframework.web.method.support.HandlerMethodArgumentResolverComposite#addResolvers(java.util.List<? extends org.springframework.web.method.support.HandlerMethodArgumentResolver>)
  2. org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#afterPropertiesSet
  3. org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#getDefaultArgumentResolvers

在这里插入图片描述

在这里插入图片描述

getCustomArgumentResolvers

那么我们断点一下,看这个方法在什么时候进行调用的呢?

  1. org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#getCustomArgumentResolvers
  2. org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#setCustomArgumentResolvers
  3. org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#requestMappingHandlerAdapter
  4. org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#getArgumentResolvers
  5. org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration#addArgumentResolvers

在这里插入图片描述

在这里插入图片描述

至此我们来到了DelegatingWebMvcConfiguration 的类,这个类的肚子里面还有一个 WebMvcConfigurerComposite mvc 的组合

在这里插入图片描述

在这里插入图片描述

我们看到是通过spring注入的方式注入进去的,那么我们只需要自己实现一个 WebMvcConfigurer的接口,那么是不是就可以进去了 。

自定义process 实现

@Configuration
public class MyWebMvcConfigurer implements WebMvcConfigurer {@AutowiredApplicationContext applicationContext;@Overridepublic void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {resolvers.add(new TimeStampResponseBodyMethodProcessor(applicationContext));}
}
public class TimeStampResponseBodyMethodProcessor implements HandlerMethodArgumentResolver {private final ApplicationContext applicationContext;private RequestResponseBodyMethodProcessor processor;public TimeStampResponseBodyMethodProcessor(ApplicationContext applicationContext) {this.applicationContext = applicationContext;}@Overridepublic boolean supportsParameter(MethodParameter parameter) {return parameter.hasParameterAnnotation(TimestampBody.class);}@Overridepublic Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {setupProcessor();Object retObj = processor.resolveArgument(parameter, mavContainer, webRequest, binderFactory);if (retObj instanceof Map) {((Map) retObj).put("timestamp",System.currentTimeMillis()+"");}return retObj;}private void setupProcessor() {if (this.processor != null) {return;}List<HandlerMethodArgumentResolver> argumentResolvers = this.applicationContext.getBean(RequestMappingHandlerAdapter.class).getArgumentResolvers();for (HandlerMethodArgumentResolver resolver : argumentResolvers) {if (resolver instanceof RequestResponseBodyMethodProcessor) {this.processor = (RequestResponseBodyMethodProcessor) resolver;return;}}}
}
查看HandlerMethodArgumentResolverComposite 是否有我们这个对象

在这里插入图片描述

测试结果

    @PostMappingpublic Map<String,Object> origin(@TimestampBody Map<String,Object> json) {return json;}

在这里插入图片描述

总结部分

​ 特别感谢bi站的u主,学java的生生,我断点不熟练,导致来回的看调用栈。视频也是看了好几遍,动手尝试,现在有一些自己的理解


文章转载自:

http://tu5U3yMh.pqryw.cn
http://Wl7DlSoN.pqryw.cn
http://vV04hGVj.pqryw.cn
http://b8Vhvxoa.pqryw.cn
http://pEkkc90Q.pqryw.cn
http://QcNPS6u8.pqryw.cn
http://R7qRX3Nb.pqryw.cn
http://0t2NVEEt.pqryw.cn
http://dJs9lkbW.pqryw.cn
http://598xTpUN.pqryw.cn
http://XN4z5dMQ.pqryw.cn
http://m0HLC4vx.pqryw.cn
http://eAzbcKHc.pqryw.cn
http://2BJKBFEc.pqryw.cn
http://K3F6Bndb.pqryw.cn
http://OEukhLDZ.pqryw.cn
http://C3lxpUAP.pqryw.cn
http://98LSyEE8.pqryw.cn
http://kKSfWTX4.pqryw.cn
http://TfWKfwZ8.pqryw.cn
http://YavgaiGq.pqryw.cn
http://8xUHvHfx.pqryw.cn
http://2mbT6Kwv.pqryw.cn
http://Z1BhBepQ.pqryw.cn
http://reaYx4Lx.pqryw.cn
http://WFUdWCrY.pqryw.cn
http://UWgEqixe.pqryw.cn
http://sItvD21o.pqryw.cn
http://pruMuJ7y.pqryw.cn
http://buPP6H8b.pqryw.cn
http://www.dtcms.com/a/386887.html

相关文章:

  • ​​解决大模型幻觉全攻略:理论、技术与落地实践​
  • qt QCandlestickSeries详解
  • 量化研究--高频日内网格T0策略研究
  • [Dify] 自动摘要与精炼:构建内容浓缩型工作流的实践指南
  • Windows安装mamba最佳实践(WSL ubuntu丝滑版)
  • 黑马头条_SpringCloud项目阶段一:环境搭建(Mac版本)
  • Java 设计模式全景解析
  • 【Python】OS模块操作目录
  • 深度学习基本模块:LSTM 长短期记忆网络
  • 初始化Vue3 项目
  • 耕地质量评价
  • MeloTTS安装实践
  • 国产化芯片ZCC3790--同步升降压控制器的全新选择, 替代LT3790
  • LeetCode 977.有序数组的平方
  • 佳易王个体诊所中西医电子处方管理系统软件教程详解:开方的时候可一键导入配方模板,自由添加模板
  • C#实现WGS-84到西安80坐标系转换的完整指南
  • rabbitmq面试题总结
  • 【Java初学基础】⭐Object()顶级父类与它的重要方法equals()
  • C语言初尝试——洛谷
  • Kaleidoscope for Mac:Mac 平台文件与图像差异对比的终极工具
  • LeetCode 刷题【80. 删除有序数组中的重复项 II】
  • 淘宝扭蛋机小程序系统开发:引领电商娱乐化潮流
  • 【车载audio开发】【基础概念2】【Usage、ContentType、Flags、SessionId之间的关系】
  • 【Day 52 】Linux-Jenkins
  • 向内核社区提交补丁
  • 【Java-常用类】
  • 在线教程丨ACL机器翻译大赛30个语种摘冠,腾讯Hunyuan-MT-7B支持33种语言翻译
  • 006 Rust基本数据类型
  • docker配置代理加速
  • 基于MATLAB的视频动态目标跟踪检测实现方案