装饰者模式
通过将对象封装在一个装饰者对象中,动态地给对象添加新的行为,增强该对象的功能,无需修改其原始代码。比如FileInputStream类和装饰者BufferedInputStream类。
FileInputStream类和装饰者BufferedInputStream类的简单实现如下:
// InputStream 接口定义
public interface InputStream {
int read();
}
// 具体的 InputStream 实现 - FileInputStream
public class FileInputStream implements InputStream {
// 实现读取文件的逻辑...
@Override
public int read() {
// 读取文件的具体逻辑
System.out.println("Reading from a file.");
return 0;
}
}
// 装饰者抽象类
public abstract class InputStreamDecorator implements InputStream {
protected InputStream inputStream;
public InputStreamDecorator(InputStream inputStream) {
this.inputStream = inputStream;
}
@Override
public int read() {
return inputStream.read();
}
}
// 具体的装饰者 - BufferedInputStream
public class BufferedInputStream extends InputStreamDecorator {
public BufferedInputStream(InputStream inputStream) {
super(inputStream);
}
@Override
public int read() {
// 添加缓存的逻辑
System.out.println("Adding buffering to InputStream.");
return super.read();
}
}
public class Client {
public static void main(String[] args) {
// 使用装饰者给 FileInputStream 添加缓存功能
InputStream fileInputStream = new FileInputStream();
InputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
// 通过装饰者链调用
bufferedInputStream.read();
}
}
这种设计模式在Java中的InputStream
家族就有广泛应用,允许通过组合方式来扩展InputStream
的功能,而无需修改基础的输入流实现。
参考:
- Spring 中的设计模式详解 | JavaGuide(Java面试 + 学习指南) 的“装饰者模式”部分
- ChatGPT
适配器模式
Spring AOP 中的适配器模式
针对Spring AOP的各种增强或者通知(Advice),使用不同的适配器生成封装了相应Advice的不同Interceptor对象,供容器使用?。
详细直接看:
-
Spring 中的设计模式详解 | JavaGuide(Java面试 + 学习指南)的“ Spring AOP 中的适配器模式”部分
-
设计模式 | 适配器模式及典型应用 - 掘金 (juejin.cn)的“spring AOP中的适配器模式”部分
Spring MVC 中的适配器模式
Spring MVC中,DispatcherServlet
根据请求信息调用 HandlerMapping
,解析请求对应的 Handler
。再根据Handler
找到对应的适配器,再由适配器执行目标Controller中的请求处理方法。
适配器模式的一个简单应用例子:每定义一个(单方法的)Controller类,都需要定义一个实现HandlerAdapter的适配器,该适配器的supports方法可进行Handler是否属于xxController的判断,handle方法可执行Controller类中的请求处理方法。(该例子的具体解释往下看)
为什么要在Spring MVC中用适配器模式?
Spring MVC 中的 Controller
种类众多,不同类型的 Controller
通过不同的方法来对请求进行处理。如果不利用适配器模式的话,DispatcherServlet
直接获取对应类型的 Controller
,需要的自行来判断,像下面这段代码一样:
if(mappedHandler.getHandler() instanceof MultiActionController){
((MultiActionController)mappedHandler.getHandler()).xxx
}else if(mappedHandler.getHandler() instanceof XXX){
...
}else if(...){
...
}
假设如果我们增加一个 HardController
,就要在代码中加入一行 if(mappedHandler.getHandler() instanceof HardController)
,这种形式就使得程序难以维护,也违反了设计模式中的开闭原则 – 对扩展开放,对修改关闭。
有了适配器模式后,Spring启动后,会将所有定义好的适配器对象存放在一个List集合中,当一个请求来临时,DispatcherServlet
会通过 handler
的类型找对应适配器(用到DispatcherServlet.getHandlerAdapter()方法), 在找适配器的过程中,会遍历List集合,针对每个适配器,都会调用supports方法进行handler instanceOf xxxController的判断,并将该适配器对象返回给用户,然后就可以统一通过适配器的 handle()
方法来调用 Controller
中的用于处理请求的方法。
每次新定义一个(单方法的)Controller类,就新定义一个相应的适配器,适配器中的supports方法中会添加if(mappedHandler.getHandler() instanceof HardController)
的类似代码,handle方法会调用相应Controller的请求处理方法。Spring启动时,只不过是多将一个适配器装入List集合中,不需要改动相应代码。这样,避免了在原有代码上添加if-else语句,只需要新定义Controller类和适配器类这两类即可,满足了修改关闭,扩展开放的开闭原则。
详细参考:
- Spring 中的设计模式详解 | JavaGuide(Java面试 + 学习指南) 的“ Spring MVC 中的适配器模式”部分
- 设计模式 | 适配器模式及典型应用 - 掘金 (juejin.cn) 的“spring MVC中的适配器模式”部分
- Spring MVC适配器的使用例子可见 SpringMVC中的适配器模式 - 写的代码很烂 - 博客园 (cnblogs.com) 的“二、模拟适配器的应用”部分
总结
通过装饰者模式的使用,可以做到不改动原代码的情况下增强对象的功能。
Spring MVC中使用适配器模式,通过适配器使用相应Controller的请求处理方法,避免了在原有代码上添加if-else语句,只需要新定义Controller类和适配器类这两个类即可,满足了修改关闭,扩展开放的开闭原则。
Spring AOP中使用适配器模式,通过适配器将一个Advice对象封装转换为相应的拦截器对象,再返回给容器。其中适配器的用法跟Spring MVC中的适配器类似,获取拦截器集合时(使用DefaultAdvisorAdapterRegistry.getInterceptors()方法),也是会遍历适配器集合,然后找到对应的适配器,生成相应的拦截器对象。通过Advice类和相应适配器类,来避免Spring MVC中不使用适配器模式会面临的在原有代码上添加if-else语句的情况,同样满足了修改关闭,扩展开放的开闭原则。
总的来讲,适配器模式的使用,减少了if-else这样大量的重复判断,让代码更加易于维护和拓展。
参考
Spring 中的设计模式详解 | JavaGuide(Java面试 + 学习指南)
设计模式 | 适配器模式及典型应用 - 掘金 (juejin.cn)
SpringMVC中的适配器模式 - 写的代码很烂 - 博客园 (cnblogs.com)
重学 Java 设计模式:实战适配器模式「从多个MQ消息体中,抽取指定字段值场景」 | 小傅哥 bugstack 虫洞栈