一、设计模式之装饰器模式介绍
装饰器模式
一、起源背景
早期扩展类功能优先使用继承 实现。继承是静态编译期扩展:子类继承父类重写方法实现增强 。当需要叠加多种独立增强能力时,会产生大量组合子类,造成类爆炸 ;同时继承强耦合父类实现,父类变更子类会受影响;并且继承无法在运行时动态增加 / 移除功能,只能编译期确定,为解决继承带来的系列问题,诞生装饰器模式思想。
简单总结早期增强类背景产生的痛点问题:
-
早期类通过继承父类重写方法实现增强
-
用于增强父类的子类过多造成类爆炸
-
子类强耦合父类,我们更期望父类变更不会影响子类。因为子类增强原理都是通过重写父类方法。
-
我们希望在运行时 能够动态 对父类增强实现增加/移除,而不是仅仅编译期
二、解决痛点
- 规避继承带来子类数量膨胀、类爆炸问题。
- 实现运行时动态给对象追加、撤销附加功能,编译期无需确定。
- 使用组合替代继承 ,降低类之间耦合,不修改原有业务类源码即可扩展能力,符合开闭原则。
- 多个增强功能可以自由排列组合叠加。(无需只通过继承父类重写方法实现增强)
三、使用场景
- 需要动态为对象增加额外职责,功能可动态增加、移除。
- 不希望使用继承做功能扩展,避免大量子类。
- 多个独立增强逻辑,需要自由组合叠加。
- JDK 示例:Java IO 流,BufferedInputStream 装饰 FileInputStream。
四、实现思想
核心:组合优于继承。
- 装饰器与被装饰对象实现同一个抽象组件接口,保证对外行为一致。
- 装饰器内部持有抽象组件对象(组合),而不是继承。
- 调用方法时委派给内部持有的原始对象 执行,在原始方法执行前后插入自定义扩展逻辑。
- 支持多层嵌套装饰,一层一层叠加增强。
五、使用步骤
- 定义抽象组件 Component :统一抽象接口,定义业务方法。
- 创建具体组件 ConcreteComponent :实现基础原始业务逻辑,即被包装的原始对象。
- 定义抽象装饰器 Decorator :实现 Component 接口 ,内部持有 Component 成员变量,接收 Component 对象。
- 编写具体装饰器 ConcreteDecorator:继承抽象装饰器,重写业务方法,在调用原始对象方法前后实现增强逻辑。
- 运行时组装:将原始对象传入装饰器 ,可多层包装,对外依旧使用 Component 接口。
六、优缺点
优点
- 遵循开闭原则,扩展不修改原有代码。
- 动态增加 / 删除功能,运行时生效,比继承灵活。
- 组合代替继承,解决类爆炸,多个增强可以自由组合。
- 原始业务类零侵入。
缺点
- 多层嵌套装饰会生成很多细小包装对象,调试排读代码难度上升。
- 对象包装层级多,理解成本提高。
七、面试背诵精简总结
装饰器属于结构型模式,为解决继承静态扩展、类爆炸问题,采用组合优于继承思想。
装饰器和被装饰对象实现同一套接口 ,装饰器内部持有组件对象 ,委派调用原有逻辑并在前后做功能增强 ;可以运行时动态叠加、移除功能,支持多层包装;
优点是灵活、符合开闭,替代继承;缺点是多层嵌套下对象多,调试复杂。
典型案例 Java IO 流。
和代理模式区分:装饰器侧重增强对象能力 ;代理侧重控制对象访问。
二、装饰器模式生产环境实现思路伪代码
实现思路伪代码:
java
java
// 1.抽象组件:统一业务接口
public interface BusinessComponent {
void doBusiness();
}
// 2.具体组件:原始业务实现类,核心业务逻辑,不做修改
public class RealBusinessComponent implements BusinessComponent{
@Override
public void doBusiness() {
// 原始核心业务逻辑
}
}
// 3.抽象装饰器,实现同一组件接口,持有组件对象,用于所有具体装饰器继承
public abstract class AbstractBusinessDecorator implements BusinessComponent{
protected final BusinessComponent delegate;
// 传入被装饰对象
public AbstractBusinessDecorator(BusinessComponent component){
this.delegate = component;
}
}
// 4.具体装饰器A:扩展能力A,如日志埋点
public class LogDecorator extends AbstractBusinessDecorator{
public LogDecorator(BusinessComponent component) {
super(component);
}
@Override
public void doBusiness() {
// 前置增强逻辑
preLog();
try{
// 委派执行原始业务
delegate.doBusiness();
// 后置成功增强
postSuccessLog();
}catch (Exception e){
// 异常增强逻辑
postErrorLog(e);
throw e;
}
}
private void preLog(){}
private void postSuccessLog(){}
private void postErrorLog(Exception e){}
}
// 5.具体装饰器B:扩展能力B,耗时统计
public class TimeCostDecorator extends AbstractBusinessDecorator{
public TimeCostDecorator(BusinessComponent component) {
super(component);
}
@Override
public void doBusiness() {
long start = System.currentTimeMillis();
try {
delegate.doBusiness();
} finally {
long cost = System.currentTimeMillis() - start;
recordCost(cost);
}
}
private void recordCost(long ms){}
}
// 6.生产组装使用,支持多层嵌套装饰
public class BusinessService{
public void handler(){
// 原始对象
BusinessComponent origin = new RealBusinessComponent();
// 多层包装,可按需动态选择是否装配装饰器
BusinessComponent component = new LogDecorator(new TimeCostDecorator(origin));
component.doBusiness();
}
}
// 生产优化点伪代码:工厂构建,可配置开关控制装饰器是否生效
public class ComponentFactory{
public BusinessComponent buildComponent(boolean enableLog, boolean enableTimeCost){
BusinessComponent component = new RealBusinessComponent();
// 这里涉及到装饰器嵌套思想
// 执行顺序:谁写在前面,谁是内层;写在后面才是最外层
// 最终对象结构:LogDecorator( TimeCostDecorator( RealBusinessComponent ) )
// 后封装的LogDecorator先执行前置逻辑,然后执行内部TimeCostDecorator前后逻辑,最后
// 执行LogDecorator的后置逻辑,这个一定要理清楚!
if(enableTimeCost){
component = new TimeCostDecorator(component);
}
if(enableLog){
component = new LogDecorator(component);
}
return component;
}
}
工厂模式开关嵌套装饰器执行顺序口诀:
先 new 出来的在内层,后 new 出来的在最外层;后封装的先执行。
生产注意
装饰器顺序会影响增强逻辑执行先后,日志、耗时、异常处理类装饰器要关注组装顺序。
例如:耗时统计放最外层,可以统计包含日志增强在内的完整耗时;耗时放内层,只统计原始业务本身耗时。
三、装饰器模式生产环境实现思路真实生产级别代码
1. 抽象组件接口、具体组件、抽象装饰器基类、具体装饰器
java
import lombok.extern.slf4j.Slf4j;
/**
* 抽象组件接口:AI会话处理组件
*/
public interface AiChatComponent {
String chat(String userQuery);
}
/**
* 具体组件:原始AI会话核心实现,只保留业务能力,无横切逻辑
*/
@Slf4j
public class RealAiChatComponent implements AiChatComponent {
@Override
public String chat(String userQuery) {
// 核心AI会话业务逻辑
return "ai response content";
}
}
/**
* 抽象装饰器基类
*/
abstract class AbstractAiChatDecorator implements AiChatComponent {
protected final AiChatComponent delegate;
public AbstractAiChatDecorator(AiChatComponent delegate) {
this.delegate = delegate;
}
}
/**
* 具体装饰器:请求日志埋点装饰器
*/
@Slf4j
public class RequestLogAiChatDecorator extends AbstractAiChatDecorator {
public RequestLogAiChatDecorator(AiChatComponent delegate) {
super(delegate);
}
@Override
public String chat(String userQuery) {
log.info("[AiChatRequest] userQuery:{}", userQuery);
try {
String resp = delegate.chat(userQuery);
log.info("[AiChatResponse] response:{}", resp);
return resp;
} catch (Exception e) {
log.error("[AiChatError] chat occur exception", e);
throw e;
}
}
}
/**
* 具体装饰器:耗时统计装饰器
*/
@Slf4j
public class TimeCostAiChatDecorator extends AbstractAiChatDecorator {
public TimeCostAiChatDecorator(AiChatComponent delegate) {
super(delegate);
}
@Override
public String chat(String userQuery) {
long start = System.currentTimeMillis();
try {
return delegate.chat(userQuery);
} finally {
long cost = System.currentTimeMillis() - start;
log.info("[AiChatCost] costMs:{}", cost);
}
}
}
2. 读取ym开关装饰器配置信息
java
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* 配置属性,通过配置文件控制装饰器是否开启
*/
@ConfigurationProperties(prefix = "ai.chat.decorator")
public class AiChatDecoratorProperties {
private boolean enableRequestLog = true;
private boolean enableTimeCost = true;
public boolean isEnableRequestLog() {
return enableRequestLog;
}
public void setEnableRequestLog(boolean enableRequestLog) {
this.enableRequestLog = enableRequestLog;
}
public boolean isEnableTimeCost() {
return enableTimeCost;
}
public void setEnableTimeCost(boolean enableTimeCost) {
this.enableTimeCost = enableTimeCost;
}
}
3. 装配工厂,根据配置动态构建多层装饰对象,生产环境Bean注册
java
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Spring装配工厂,根据配置动态构建多层装饰对象,生产环境Bean注册
*/
@Configuration
@EnableConfigurationProperties(AiChatDecoratorProperties.class)
public class AiChatComponentConfig {
@Bean
public AiChatComponent aiChatComponent(AiChatDecoratorProperties properties) {
AiChatComponent component = new RealAiChatComponent();
// 先组装内层装饰器
if (properties.isEnableTimeCost()) {
component = new TimeCostAiChatDecorator(component);
}
// 后组装外层装饰器
if (properties.isEnableRequestLog()) {
component = new RequestLogAiChatDecorator(component);
}
return component;
}
}
4. yml配置开关信息
java
# application.yml
ai:
chat:
decorator:
enable-request-log: true
enable-time-cost: true
5. 业务使用层,面向抽象接口编程,对上层屏蔽装饰细节
java
/**
* 业务使用层,面向抽象接口编程,对上层屏蔽装饰细节
*/
@Service
public class AiChatBusinessService {
private final AiChatComponent aiChatComponent;
public AiChatBusinessService(AiChatComponent aiChatComponent) {
this.aiChatComponent = aiChatComponent;
}
public String handleChat(String query) {
return aiChatComponent.chat(query);
}
}
生产关键点说明(仅文字)
- 面向接口编程 ,业务层只依赖抽象
AiChatComponent,完全感知不到装饰器实现; - 通过 Spring 配置驱动开关,环境可灵活开启关闭横切增强,无需修改业务代码;
- 组装顺序:先 new 的装饰器在内层,后 new 的为最外层,直接影响执行时序;
- 异常向上透传,装饰器只做埋点日志,不吞业务异常;
- 装饰器内部
delegate使用 final,防止运行时引用被篡改; - 新增横切能力只新增
AbstractAiChatDecorator子类 ,不改动原有代码,符合开闭原则。