设计模式(二)装饰器模式、代理模式(待更新)

一、设计模式之装饰器模式介绍

装饰器模式

一、起源背景

早期扩展类功能优先使用继承 实现。继承是静态编译期扩展:子类继承父类重写方法实现增强 。当需要叠加多种独立增强能力时,会产生大量组合子类,造成类爆炸 ;同时继承强耦合父类实现,父类变更子类会受影响;并且继承无法在运行时动态增加 / 移除功能,只能编译期确定,为解决继承带来的系列问题,诞生装饰器模式思想。

简单总结早期增强类背景产生的痛点问题:

  1. 早期类通过继承父类重写方法实现增强

  2. 用于增强父类的子类过多造成类爆炸

  3. 子类强耦合父类,我们更期望父类变更不会影响子类。因为子类增强原理都是通过重写父类方法。

  4. 我们希望在运行时 能够动态 对父类增强实现增加/移除,而不是仅仅编译期

二、解决痛点

  1. 规避继承带来子类数量膨胀、类爆炸问题。
  2. 实现运行时动态给对象追加、撤销附加功能,编译期无需确定。
  3. 使用组合替代继承 ,降低类之间耦合,不修改原有业务类源码即可扩展能力,符合开闭原则
  4. 多个增强功能可以自由排列组合叠加。(无需只通过继承父类重写方法实现增强)

三、使用场景

  1. 需要动态为对象增加额外职责,功能可动态增加、移除。
  2. 不希望使用继承做功能扩展,避免大量子类
  3. 多个独立增强逻辑,需要自由组合叠加
  4. JDK 示例:Java IO 流,BufferedInputStream 装饰 FileInputStream。

四、实现思想

核心:组合优于继承

  1. 装饰器与被装饰对象实现同一个抽象组件接口,保证对外行为一致
  2. 装饰器内部持有抽象组件对象(组合),而不是继承。
  3. 调用方法时委派给内部持有的原始对象 执行,在原始方法执行前后插入自定义扩展逻辑
  4. 支持多层嵌套装饰,一层一层叠加增强。

五、使用步骤

  1. 定义抽象组件 Component :统一抽象接口,定义业务方法
  2. 创建具体组件 ConcreteComponent :实现基础原始业务逻辑,即被包装的原始对象
  3. 定义抽象装饰器 Decorator实现 Component 接口 ,内部持有 Component 成员变量,接收 Component 对象
  4. 编写具体装饰器 ConcreteDecorator:继承抽象装饰器,重写业务方法,在调用原始对象方法前后实现增强逻辑。
  5. 运行时组装:将原始对象传入装饰器可多层包装,对外依旧使用 Component 接口。

六、优缺点

优点

  1. 遵循开闭原则,扩展不修改原有代码。
  2. 动态增加 / 删除功能,运行时生效,比继承灵活。
  3. 组合代替继承,解决类爆炸,多个增强可以自由组合。
  4. 原始业务类零侵入

缺点

  1. 多层嵌套装饰会生成很多细小包装对象,调试排读代码难度上升
  2. 对象包装层级多,理解成本提高

七、面试背诵精简总结

装饰器属于结构型模式,为解决继承静态扩展、类爆炸问题,采用组合优于继承思想。

装饰器和被装饰对象实现同一套接口 ,装饰器内部持有组件对象 ,委派调用原有逻辑并在前后做功能增强 ;可以运行时动态叠加、移除功能,支持多层包装

优点是灵活、符合开闭,替代继承;缺点是多层嵌套下对象多,调试复杂。

典型案例 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);
    }
}

生产关键点说明(仅文字)

  1. 面向接口编程 ,业务层只依赖抽象AiChatComponent,完全感知不到装饰器实现;
  2. 通过 Spring 配置驱动开关,环境可灵活开启关闭横切增强,无需修改业务代码;
  3. 组装顺序:先 new 的装饰器在内层,后 new 的为最外层,直接影响执行时序;
  4. 异常向上透传,装饰器只做埋点日志,不吞业务异常;
  5. 装饰器内部delegate使用 final,防止运行时引用被篡改
  6. 新增横切能力只新增AbstractAiChatDecorator子类 ,不改动原有代码,符合开闭原则
相关推荐
秋饼9 小时前
LangChain4j + Java 实现企业级 Text-to-SQL 智能问数系统:从自然语言到安全可控的数据洞察
java·ai·技术分享·后端开发
AI人工智能+电脑小能手1 天前
大白话说Java设计模式-28-模板方法模式(业务实战篇)
java·设计模式·模板方法模式·spring源码·订单系统·代码复用
leeyi1 天前
两个 Agent 怎么协作:Host-Worker 模式(第91篇-E77)
设计模式·agent·ai编程
AI人工智能+电脑小能手2 天前
大白话说Java设计模式-26-策略模式(业务实战篇)
java·spring·设计模式·策略模式·支付系统·算法切换
青 春 记 忆2 天前
零基础入门Python15|关联、聚合、索引与事务:订单数据库
开发语言·python·后端开发
dogstarhuang2 天前
GPT-5.6 API 分三档收费:Sol/Terra/Luna 选型指南与成本测算(附路由代码)
开发语言·人工智能·python·后端开发·接口开发·api定价·接口管理平台
Javatutouhouduan2 天前
Java初学者如何高效学习JVM?
java·jvm·java虚拟机·java面试·后端开发·java程序员·java八股文
星火10242 天前
【Groovy翻译-进阶篇】Groovy 中的设计模式
后端·设计模式·groovy
AI人工智能+电脑小能手2 天前
大白话说Java设计模式-27-策略模式(源码剖析篇)
java·spring·设计模式·策略模式·源码分析·事务管理·comparator