设计模式之装饰器模式
装饰器模式(Decorator)依然是我们设计模式中的结构型模式,其中的构造思想仍然是对多个类进行组合使用,以达成系统调用实现指定功能的设计模式。装饰器模式不论在我们日常开发过程中还是在我们提升技术阅读源码过程中都是比较常见的,但是整体学习这个模式的思路难度不大,接下来我将详细讲解此设计模式。
目录
1. 概念
2. 代码实现
3. 应用场景
4. 装饰器模式与代理模式的区别
1. 概念
我们前期所讲到的适配器模式,是连接两个类,那么我们今天提到的装饰器模式,就是在一个类的基础上去增强它。向一个现有的对象添加新的功能,同时又不改变其结构。即创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
其中在装饰器模式中我们有这么几个重要对象需要进行关注:
- 抽象构件(Component)角色: 定义一个抽象接口以规范准备接收附加责任的对象。
- 具体构件(ConcreteComponent)角色: 实现抽象构件,通过装饰角色为其添加一些职责。
- 抽象装饰(Decorator)角色: 继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
- 具体装饰(ConcreteDecorator)角色: 实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。
2. 代码实现
这里我们按照上方的类图来实现一个TikTok直播增强的功能,我们首先抽象出来ManTikTok类,其中有两个其他类实现该抽象构件:
ManTikTok.java
/**
* 抽象构建
*/
public interface ManTikTok {
void tiktok();
}
ATikTok.java
public class ATikTok implements ManTikTok{
@Override
public void tiktok() {
System.out.println("A,I'm tiktok.... ");
}
}
BTikTok.java
public class BTikTok implements ManTikTok{
@Override
public void tiktok() {
System.out.println("B,I'm tiktok.... ");
}
}
这时我们想在ManTikTok的tiktok方法进行加强,想让他在执行的过程中增加美颜的效果,此时我们可以抽象出来一个TikTokDecorator构件,并有一个MeiYanTikTokDecorator实现了此抽象类,实现其中的加强方法。注意:我们这个Decorator类必须要继承被增强类,这是其设计模式的重要关注点,这样我们就可以拿到被增强类的增强方法,在这个案例中就是我们的tiktok方法。
TiktokDecorator.java
/**
* 抽象装饰器
* 抖音直播装饰器
*/
public interface TiktokDecorator extends ManTikTok{
void enable();
}
MeiYanDecorator.java
public class MeiYanDecorator implements TiktokDecorator{
private ManTikTok manTikTok;
public MeiYanDecorator(ManTikTok manTikTok){
this.manTikTok = manTikTok;
}
@Override
public void tiktok() {
//开启美颜
enable();
//我开始直播
manTikTok.tiktok();
}
/**
* 定义的增强功能
*/
@Override
public void enable() {
System.out.println("看这个帅哥.....");
System.out.println("花花花花花花花花花花花");
}
}
此时我们讲解一下这个MeiYanDecorator类,我们这个类继承了TikTokDecorator接口,那么我们其实就拥有了enable方法以及tiktok方法,我们的目的是要将被增强的类中的tiktok方法进行加强,那么此时我们就要拿到被增强类的方法,那么我们就可以直接通过属性注入的方式,将被增强类引入,从而编写该类的tiktok方法时,对其进行加强(我们的加强逻辑直接封装在enable()中),那么外部系统调用我们的时候,直接调用MeiYanDecorator类,传入被增强类进行包装,即可增强原来的方法。
MainTest.java
public class MainTest {
public static void main(String[] args) {
//被装饰对象
ManTikTok manTikTok = new LeiTikTok();
// manTikTok.tiktok();
/**
* LiMingTiktokProxy proxy = new LiMingTiktokProxy(new LeiTikTok());
* proxy.tiktok();
*/
MeiYanDecorator decorator = new MeiYanDecorator(manTikTok);
decorator.tiktok();
}
}
3. 应用场景
- SpringSession中如何进行session与redis关联
- HttpRequestWrapper session
- MyBatisPlus提取了QueryWrapper
- Spring中的BeanWrapper
- Spring Webflux中的 WebHandlerDecorator
- 已存的类,每一天在某个功能使用的时候发现不够,就可以装饰器。
- ......
4. 装饰器模式与代理模式的区别
其实这是我在学习装饰者模式提出的一个问题,作为一个Java开发者来说,尤其是作为一个Spring工程师来说,代理模式不论是动态代理还是静态代理引用的相对来说都比较多,但是我会发现其实装饰器模式和静态代理很像很像,这里我可以给出的一个结论是:在有些特殊情况下,其实装饰器模式和静态代理模式可以画上约等于号,这里我们引用一下ChatGPT给出的二者的区别:
方面 | 静态代理(Static Proxy) | 装饰者模式(Decorator Pattern) |
---|---|---|
主要目的 | 控制对目标对象的访问,可能是权限控制、日志记录、事务管理等 | 动态扩展对象功能,增强行为(如日志、加密、缓存) |
是否改变对象核心行为 | 通常不改变,只是控制访问或添加额外逻辑 | 可以改变对象的核心行为(增强、修改、扩展) |
结构特点 | 代理类通常直接持有目标对象的引用,实现与目标对象相同的接口 | 装饰器类持有被装饰对象的引用,可以叠加多个装饰器 |
是否支持多个包装 | 通常不支持(一个代理类只能代理一个对象) | 支持多层装饰(多个装饰器可以嵌套使用) |
典型应用 | 远程代理、权限控制(如 Spring AOP 静态代理) | Java I/O 流、GUI 组件、自定义日志 |