设计模式理论与实战手册

第一章 前言

1.1 手册定位

本手册打破“仅罗列模式用法”的传统思路,以架构设计视角为核心,先构建设计模式的完整理论体系(定义、核心原则、分类逻辑),再针对每一种常用设计模式,从“设计思想→架构定位→实战场景→代码落地→注意事项”五个维度展开,实现“理论指导实践、实践反哺架构理解”的目标。

手册面向后端开发工程师、架构师,既适合初学者快速掌握设计模式的核心逻辑,也适合资深开发者梳理架构设计中的模式选型思路,为项目的可扩展性、可维护性、复用性提供设计支撑,助力打造高内聚、低耦合的系统架构。

1.2 设计模式的核心价值

设计模式并非“语法技巧”,而是经过长期实践沉淀的、解决特定架构问题的“通用方案”,其核心价值体现在三个层面,贯穿系统设计、开发、维护全流程:

  • 解耦架构:降低模块间的依赖,实现“高内聚、低耦合”,便于后续架构迭代和模块替换。

  • 提升复用:将通用逻辑、核心流程抽象为可复用的模式,减少重复编码,降低开发成本。

  • 增强可维护:统一设计规范,让代码逻辑清晰、可读性强,便于团队协作和后续问题排查、功能扩展。

1.3 适用范围与版本适配

  • 适用场景:系统架构设计、模块封装、代码重构、第三方组件开发、SpringBoot/SpringCloud 项目落地。

  • 适配语言:以 Java 为主(贴合 Spring 生态),核心思想可迁移至其他面向对象语言(如 Python、Go)。

  • 适配框架:SpringBoot 2.x/3.x、SpringCloud,案例均贴合企业级实战场景,可直接复用或修改。

第二章 设计模式核心理论基础

设计模式的本质是“面向对象设计原则的落地”,所有模式的设计都围绕核心原则展开。在学习具体模式前,需先掌握设计模式的基础理论,理解其设计思想的根源,避免“死记硬背模式代码”。

2.1 设计模式的定义

设计模式(Design Pattern)是在特定场景下,解决某一类重复出现的架构问题的成熟方案。它不是一段可直接复制的代码,而是一种“设计思想”“解决方案模板”,可根据具体业务场景灵活调整实现细节。

核心特点:可复用、可扩展、可维护、贴合面向对象设计原则,是“理论+实践”的结合体。

2.2 面向对象设计七大原则

设计模式的所有设计思路,都源于这七大原则——它们是架构设计的“底线”,确保系统具备良好的扩展性和可维护性。以下结合架构视角,简要说明 each 原则的核心思想及实践意义:

设计原则核心思想架构实践意义
单一职责原则(SRP)一个类/模块只负责一项职责,职责单一降低模块耦合,便于单独维护和扩展(如拆分业务模块、工具类)
开放-封闭原则(OCP)对扩展开放,对修改关闭避免修改原有代码引发风险,通过扩展实现新功能(架构迭代的核心原则)
里氏替换原则(LSP)子类可替换父类,且不影响程序正确性保障继承关系的合理性,支持多态,便于模块替换(如接口实现类的切换)
依赖倒置原则(DIP)依赖抽象,不依赖具体实现;面向接口编程降低模块间的直接依赖,提升架构灵活性(如 Spring 依赖注入的核心思想)
接口隔离原则(ISP)拆分庞大接口为多个小型接口,接口职责单一避免接口冗余,减少模块间的不必要依赖(如微服务接口设计)
迪米特法则(LOD)一个对象尽可能少地了解其他对象,降低耦合减少模块间的通信成本,避免“牵一发而动全身”(如封装工具类、中间层)
合成复用原则(CRP)优先使用组合/聚合,而非继承实现复用避免继承带来的耦合,提升代码复用的灵活性(如 Spring 中 Bean 的组合)

核心总结:七大原则的核心目标是“解耦”,所有设计模式都是这些原则的具体落地——比如工厂模式体现依赖倒置原则,装饰器模式体现开放-封闭原则。

2.3 设计模式的分类

根据设计模式解决的“架构问题类型”,可将其分为三大类,每类对应不同的架构场景,便于选型时快速定位:

  1. 创建型模式(5种):解决“对象创建”的架构问题,封装对象创建的细节,降低创建逻辑与业务逻辑的耦合,提升对象创建的灵活性。核心场景:对象创建复杂、需要统一管理创建逻辑(如 Spring Bean 的创建)。

  2. 结构型模式(7种):解决“对象/模块组合”的架构问题,通过合理的组合方式,优化系统结构,提升模块的复用性和扩展性。核心场景:模块间耦合过高、需要灵活组合多个模块(如微服务接口适配)。

  3. 行为型模式(11种):解决“对象/模块间通信、行为协作”的架构问题,规范对象间的交互方式,降低通信耦合,提升行为的可复用性和可扩展性。核心场景:多个模块需要协同工作、行为逻辑复杂(如任务调度、事件通知)。

注:本手册聚焦企业级实战中最常用的15种设计模式,重点讲解其架构定位和落地方案,避免冗余的理论堆砌。

第三章 创建型模式:对象创建的标准化

创建型模式的核心架构定位:将对象的创建逻辑与业务逻辑分离,由专门的“创建器”负责对象创建,统一管理创建流程(如对象初始化、依赖注入),避免业务代码中嵌入复杂的创建逻辑,提升架构的灵活性和可维护性。

核心设计思想:遵循“依赖倒置原则”,面向抽象创建对象,而非直接 new 具体类,便于后续替换对象实现。

3.1 单例模式(Singleton Pattern)

3.1.1 设计思想与架构定位

核心思想:确保一个类在整个系统中只有一个实例,并提供一个全局唯一的访问入口,避免重复创建对象导致的资源浪费(如连接池、日志对象)。

架构定位:解决“对象重复创建”的问题,适用于全局共享、无状态的对象(如工具类、配置类、连接池),是架构中“资源复用”的基础模式。

3.1.2 实战场景

  • Spring 容器中的 Bean 默认是单例模式(无状态 Bean),全局复用一个实例,减少资源消耗。

  • 系统中的日志工具类、配置工具类、连接池(如数据库连接池、Redis 连接池),需保证全局唯一实例。

  • 计数器、全局缓存等,需保证数据一致性,避免多实例导致的数据混乱。

3.1.3 实战代码(企业级推荐:双重检查锁+volatile)

单例模式有多种实现方式,企业级实战中优先选择“双重检查锁+volatile”,兼顾线程安全、性能和懒加载,避免饿汉式的资源浪费和懒汉式的线程安全问题。

/**
 * 单例模式实战:全局日志工具类(线程安全、懒加载、高性能)
 * 架构说明:全局唯一实例,避免重复创建日志对象,统一日志输出逻辑
 */
public class LogUtils {
    // volatile 防止指令重排,确保 instance 初始化完成后再被访问
    private static volatile LogUtils instance;

    // 私有构造方法,禁止外部 new 实例
    private LogUtils() {
        // 初始化日志配置(如加载日志文件、设置日志级别)
        initLogConfig();
    }

    // 双重检查锁,兼顾线程安全和性能
    public static LogUtils getInstance() {
        if (instance == null) { // 第一次检查,避免不必要的锁竞争
            synchronized (LogUtils.class) { // 加锁,保证线程安全
                if (instance == null) { // 第二次检查,防止多线程同时进入后重复创建
                    instance = new LogUtils();
                }
            }
        }
        return instance;
    }

    // 日志初始化逻辑(业务细节)
    private void initLogConfig() {
        System.out.println("初始化日志配置,全局唯一实例");
    }

    // 日志输出方法(业务逻辑)
    public void info(String message) {
        System.out.println("[INFO] " + message);
    }
}

Spring 中的应用:Spring 容器通过 @Bean 注解默认创建单例 Bean,底层通过单例模式管理 Bean 实例,开发者可通过 @Scope("singleton") 显式指定(默认就是单例)。

3.1.4 注意事项

  • 避免单例对象持有状态:单例对象是全局共享的,若持有状态(如成员变量),多线程环境下会导致数据安全问题,建议单例对象仅提供无状态方法(如工具类)。

  • 避免滥用单例:并非所有类都适合单例,若对象需要频繁创建、销毁,且资源消耗低(如普通实体类),不建议使用单例,否则会降低系统灵活性。

  • 序列化问题:若单例类实现 Serializable 接口,需重写 readResolve() 方法,避免反序列化创建新实例。

3.2 工厂模式(Factory Pattern)

工厂模式分为三种:简单工厂模式、工厂方法模式、抽象工厂模式,核心思想一致,复杂度逐步提升,适配不同的架构场景。重点讲解企业级实战中最常用的工厂方法模式抽象工厂模式

3.2.1 设计思想与架构定位

核心思想:定义一个“创建对象的接口”,由子类决定具体创建哪个对象,将对象创建的逻辑延迟到子类,实现“创建与使用分离”。

架构定位:解决“对象创建逻辑复杂、需要灵活切换对象实现”的问题,遵循依赖倒置原则,面向抽象编程,提升架构的扩展性(如切换第三方组件、适配不同业务场景)。

3.2.2 工厂方法模式

实战首选

3.2.3 实战场景

系统需要对接多种支付方式(微信支付、支付宝支付、银联支付),每种支付方式的创建逻辑不同,但对外提供统一的支付接口,需要灵活切换支付方式,且便于后续新增支付方式(如新增苹果支付)。

3.2.4 实战代码(SpringBoot 场景)

// 1. 抽象产品:支付接口(面向抽象,定义统一行为)
public interface Payment {
    // 统一支付方法
    String pay(double amount);
}

// 2. 具体产品:微信支付(实现抽象接口)
public class WeChatPayment implements Payment {
    @Override
    public String pay(double amount) {
        return "微信支付:" + amount + "元,支付成功";
    }
}

// 3. 具体产品:支付宝支付(实现抽象接口)
public class AlipayPayment implements Payment {
    @Override
    public String pay(double amount) {
        return "支付宝支付:" + amount + "元,支付成功";
    }
}

// 4. 抽象工厂:支付工厂接口(定义创建产品的接口)
public interface PaymentFactory {
    Payment createPayment();
}

// 5. 具体工厂:微信支付工厂(创建微信支付实例)
public class WeChatPaymentFactory implements PaymentFactory {
    @Override
    public Payment createPayment() {
        // 微信支付的创建逻辑(如初始化配置、注入依赖)
        return new WeChatPayment();
    }
}

// 6. 具体工厂:支付宝支付工厂(创建支付宝支付实例)
public class AlipayPaymentFactory implements PaymentFactory {
    @Override
    public Payment createPayment() {
        // 支付宝支付的创建逻辑
        return new AlipayPayment();
    }
}

// 7. 业务层:使用工厂创建支付实例(无需关注具体创建逻辑)
@Service
public class PaymentService {
    // 根据支付类型,获取对应的工厂,创建支付实例
    public String doPay(String paymentType, double amount) {
        PaymentFactory factory;
        switch (paymentType) {
            case "wechat":
                factory = new WeChatPaymentFactory();
                break;
            case "alipay":
                factory = new AlipayPaymentFactory();
                break;
            default:
                throw new IllegalArgumentException("不支持的支付方式");
        }
        Payment payment = factory.createPayment();
        return payment.pay(amount);
    }
}

3.2.5 架构优势

新增支付方式(如银联支付)时,只需新增“银联支付产品类”和“银联支付工厂类”,无需修改原有业务代码,完全符合“开放-封闭原则”,降低架构迭代风险。

3.3 抽象工厂模式

复杂场景适配

当系统需要创建“一组相关联的产品”(而非单个产品)时,使用抽象工厂模式。例如:支付系统中,除了支付方式,还有退款方式、对账方式,每种支付方式对应一套退款、对账逻辑,需要统一创建这一组产品。

3.3.1 注意事项

  • 工厂模式适合“产品种类固定、新增产品频繁”的场景,若产品种类极少且不常变化,无需使用工厂模式(避免过度设计)。

  • 结合 Spring 依赖注入:企业级实战中,可将工厂类、产品类注入 Spring 容器,通过 @Autowired 获取,无需手动 new 实例,提升架构的灵活性。

  • 避免工厂类过于复杂:每个工厂只负责创建一类产品,遵循单一职责原则,避免一个工厂创建多种不相关的产品。

3.4 建造者模式(Builder Pattern)

3.4.1 设计思想与架构定位

核心思想:将复杂对象的创建过程拆解为多个步骤,通过“建造者”逐步构建对象,允许灵活配置对象的各个属性,避免创建过程中出现大量构造方法(如参数过多的构造器)。

架构定位:解决“复杂对象创建”的问题,适用于对象属性多、构造复杂、需要灵活配置的场景(如实体类、配置对象、DTO 构建),提升代码的可读性和可维护性。

3.4.2 实战场景

  • 用户注册DTO、订单DTO等,属性较多(如用户名、手机号、邮箱、地址等),需要灵活配置部分属性,避免构造器参数过多。

  • 复杂配置对象(如线程池配置、连接池配置),属性多且有默认值,需要支持自定义配置。

  • Spring 中的 Bean 构建(如 @Builder 注解,底层基于建造者模式)。

3.4.3 实战代码(Lombok 简化+原生实现)

企业级实战中,可使用 Lombok 的 @Builder 注解快速实现建造者模式,简化代码;若需自定义建造逻辑,可手动实现建造者。

// 方式1:Lombok @Builder 简化实现(推荐,减少重复代码)
import lombok.Builder;
import lombok.Data;

/**
 * 订单DTO(复杂对象,属性较多)
 */
@Data
@Builder
public class OrderDTO {
    private Long orderId;
    private String userId;
    private double amount;
    private String orderStatus;
    private String address;
    private String phone;
    // 更多属性...

    // Lombok 自动生成建造者方法,使用方式:
    // OrderDTO order = OrderDTO.builder()
    //         .orderId(1L)
    //         .userId("1001")
    //         .amount(99.9)
    //         .build();
}

// 方式2:手动实现建造者模式(自定义建造逻辑,如属性校验)
public class OrderDTO {
    private Long orderId;
    private String userId;
    private double amount;
    private String orderStatus;

    // 私有构造方法,只能通过建造者创建
    private OrderDTO(Builder builder) {
        this.orderId = builder.orderId;
        this.userId = builder.userId;
        this.amount = builder.amount;
        this.orderStatus = builder.orderStatus;
        // 自定义校验逻辑(如金额不能为负)
        validate();
    }

    // 校验逻辑
    private void validate() {
        if (amount < 0) {
            throw new IllegalArgumentException("订单金额不能为负");
        }
        if (userId == null || userId.isEmpty()) {
            throw new IllegalArgumentException("用户ID不能为空");
        }
    }

    // 建造者内部类
    public static class Builder {
        private Long orderId;
        private String userId;
        private double amount;
        private String orderStatus;

        // 链式调用方法
        public Builder orderId(Long orderId) {
            this.orderId = orderId;
            return this;
        }

        public Builder userId(String userId) {
            this.userId = userId;
            return this;
        }

        public Builder amount(double amount) {
            this.amount = amount;
            return this;
        }

        public Builder orderStatus(String orderStatus) {
            this.orderStatus = orderStatus;
            return this;
        }

        // 构建对象
        public OrderDTO build() {
            return new OrderDTO(this);
        }
    }

    // 使用方式
    public static void main(String[] args) {
        OrderDTO order = new OrderDTO.Builder()
                .orderId(1L)
                .userId("1001")
                .amount(99.9)
                .orderStatus("PENDING")
                .build();
    }
}

3.4.4 注意事项

  • 建造者模式适合“属性多、构造复杂”的对象,若对象属性少(3个以内),无需使用,避免过度设计。

  • 添加属性校验:在建造者的 build() 方法或对象的构造方法中添加属性校验,确保对象创建的合法性。

  • 与工厂模式区分:工厂模式侧重“创建不同类型的对象”,建造者模式侧重“创建同一类型、不同配置的对象”。

3.5 原型模式(Prototype Pattern)

3.5.1 设计思想与架构定位

核心思想:通过“复制现有对象”来创建新对象,而非重新 new 实例,减少对象创建的开销(尤其是对象初始化复杂、耗时的场景)。

架构定位:解决“对象创建耗时、资源消耗高”的问题,适用于频繁创建相似对象的场景(如批量创建订单、批量生成报表数据)。

3.5.2 实战场景

  • 批量生成订单数据:订单对象初始化需要查询用户信息、商品信息,频繁 new 实例会消耗大量资源,通过复制原型对象提升效率。

  • 缓存对象复制:缓存中存储的对象,需要频繁获取并修改部分属性,通过复制原型避免修改原缓存对象。

  • Spring 中的 Bean 作用域(prototype),每次获取 Bean 时,复制一个新实例。

3.5.3 实战代码(深拷贝+浅拷贝)

原型模式分为浅拷贝和深拷贝,浅拷贝仅复制对象本身,不复制引用类型属性;深拷贝复制对象本身及所有引用类型属性,企业级实战中多使用深拷贝,避免数据安全问题。

import java.io.*;

/**
 * 原型模式实战:订单原型(深拷贝实现)
 */
public class OrderPrototype implements Cloneable, Serializable {
    private Long orderId;
    private String userId;
    private Product product; // 引用类型属性(需要深拷贝)

    // 浅拷贝(默认 clone() 方法,不复制引用类型)
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    // 深拷贝(通过序列化实现,复制所有属性,包括引用类型)
    public OrderPrototype deepClone() {
        try {
            // 序列化:将对象写入流
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(this);

            // 反序列化:从流中读取对象(新实例)
            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
            return (OrderPrototype) ois.readObject();
        } catch (IOException | ClassNotFoundException e) {
            throw new RuntimeException("深拷贝失败", e);
        }
    }

    // 静态内部类:引用类型属性(需实现 Serializable,否则深拷贝失败)
    static class Product implements Serializable {
        private Long productId;
        private String productName;

        // getter/setter 省略
    }

    // 实战使用
    public static void main(String[] args) throws CloneNotSupportedException {
        // 1. 创建原型对象
        OrderPrototype prototype = new OrderPrototype();
        prototype.setOrderId(1L);
        prototype.setUserId("1001");
        Product product = new Product();
        product.setProductId(100L);
        product.setProductName("手机");
        prototype.setProduct(product);

        // 2. 深拷贝创建新对象
        OrderPrototype newOrder = prototype.deepClone();

        // 3. 修改新对象的引用属性,不会影响原型对象(深拷贝特性)
        newOrder.getProduct().setProductName("电脑");
        System.out.println(prototype.getProduct().getProductName()); // 输出:手机
        System.out.println(newOrder.getProduct().getProductName()); // 输出:电脑
    }

    // getter/setter 省略
}

3.5.4 注意事项

  • 引用类型属性需支持深拷贝:若对象包含引用类型属性,需实现序列化(或手动复制引用对象),否则深拷贝会失败,导致数据关联问题。

  • 避免滥用原型模式:若对象创建成本低、属性简单,无需使用原型模式,直接 new 实例更高效。

  • 结合缓存:将常用的原型对象缓存起来,需要时直接复制,进一步提升效率(如缓存模板对象)。

第四章 结构型模式:模块组合的优化

结构型模式的核心架构定位:优化对象/模块的组合方式,通过合理的组合、继承、代理等方式,解决模块间耦合过高、功能扩展困难的问题,提升系统架构的灵活性和复用性。

核心设计思想:遵循“合成复用原则”,优先通过组合/聚合实现模块复用,而非继承,降低模块间的耦合。

4.1 代理模式(Proxy Pattern)

4.1.1 设计思想与架构定位

核心思想:为目标对象创建一个“代理对象”,代理对象负责拦截目标对象的方法调用,在方法执行前后添加额外逻辑(如日志、权限校验、缓存),而不修改目标对象本身。

架构定位:解决“在不修改原有代码的前提下,为目标对象添加额外功能”的问题,是 AOP(面向切面编程)的核心实现模式,适用于日志、权限、缓存、事务等横切逻辑的统一处理。

4.1.2 实战场景

  • 接口权限校验:在调用目标接口前,通过代理对象校验用户权限,无权限则拒绝调用。

  • 方法调用日志:通过代理对象记录方法的入参、出参、调用耗时,便于问题排查。

  • 缓存增强:在调用目标方法前,先查询缓存,缓存存在则直接返回,不存在则调用目标方法并缓存结果。

  • Spring AOP:底层基于动态代理模式(JDK 动态代理、CGLIB 动态代理),实现横切逻辑的统一管理。

4.1.3 实战代码(JDK 动态代理+Spring AOP 落地)

代理模式分为静态代理和动态代理,企业级实战中优先使用动态代理(无需手动创建代理类,提升开发效率),JDK 动态代理基于接口,CGLIB 动态代理基于子类。

// 1. 目标接口(JDK 动态代理必须基于接口)
public interface UserService {
    String getUserById(String userId);
}

// 2. 目标对象(业务逻辑实现)
@Service
public class UserServiceImpl implements UserService {
    @Override
    public String getUserById(String userId) {
        // 模拟数据库查询
        System.out.println("查询用户信息,userId:" + userId);
        return "用户:" + userId + ",姓名:张三";
    }
}

// 3. 动态代理处理器(实现 InvocationHandler,添加横切逻辑)
public class LogProxyHandler implements InvocationHandler {
    // 目标对象
    private Object target;

    public LogProxyHandler(Object target) {
        this.target = target;
    }

    // 拦截目标方法调用,添加日志逻辑
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 方法执行前:记录入参
        System.out.println("方法 " + method.getName() + " 入参:" + args[0]);
        long start = System.currentTimeMillis();

        // 调用目标方法
        Object result = method.invoke(target, args);

        // 方法执行后:记录出参、耗时
        long cost = System.currentTimeMillis() - start;
        System.out.println("方法 " + method.getName() + " 出参:" + result);
        System.out.println("方法调用耗时:" + cost + "ms");

        return result;
    }
}

// 4. 代理工厂(创建代理对象)
public class ProxyFactory {
    public static Object createProxy(Object target) {
        // JDK 动态代理:创建代理对象
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(), // 类加载器
                target.getClass().getInterfaces(), // 目标对象实现的接口
                new LogProxyHandler(target) // 代理处理器
        );
    }
}

// 5. 测试使用
public class ProxyTest {
    public static void main(String[] args) {
        // 目标对象
        UserService userService = new UserServiceImpl();
        // 创建代理对象
        UserService proxy = (UserService) ProxyFactory.createProxy(userService);
        // 调用代理对象方法(触发拦截逻辑)
        proxy.getUserById("1001");
    }
}

// 6. Spring AOP 简化实现(企业级首选,无需手动创建代理)
@Aspect
@Component
public class LogAspect {
    // 切入点:拦截 UserService 所有方法
    @Pointcut("execution(* com.example.service.UserService.*(..))")
    public void logPointcut() {}

    // 环绕通知:添加日志逻辑(底层自动生成代理对象)
    @Around("logPointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        // 方法执行前
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("方法 " + methodName + " 入参:" + args[0]);
        long start = System.currentTimeMillis();

        // 调用目标方法
        Object result = joinPoint.proceed();

        // 方法执行后
        long cost = System.currentTimeMillis() - start;
        System.out.println("方法 " + methodName + " 出参:" + result);
        System.out.println("方法调用耗时:" + cost + "ms");

        return result;
    }
}

4.1.4 注意事项

  • 代理模式的核心是“不修改目标对象”,所有额外逻辑都在代理对象中实现,符合开放-封闭原则。

  • JDK 动态代理与 CGLIB 选择:若目标对象实现了接口,优先使用 JDK 动态代理;若目标对象无接口,使用 CGLIB 动态代理(Spring AOP 自动适配)。

  • 避免代理链过长:若多个代理对象嵌套(如日志代理→权限代理→缓存代理),会增加系统开销,建议合并横切逻辑,简化代理链。

4.2 装饰器模式(Decorator Pattern)

4.2.1 设计思想与架构定位

核心思想:动态地给目标对象添加额外功能,通过“装饰器类”包裹目标对象,装饰器与目标对象实现同一接口,既保留目标对象的原有功能,又可灵活添加新功能,支持多层装饰。

架构定位:解决“动态扩展对象功能”的问题,适用于需要为对象添加多种可选功能、且功能可组合的场景(如文件读写、接口增强),与代理模式的区别是:装饰器模式侧重“功能扩展”,代理模式侧重“功能拦截”。

4.2.2 实战场景

  • 文件读写增强:基础文件读写功能,可通过装饰器添加“加密/解密”“日志记录”“缓存”等功能,且功能可组合(如先加密再日志)。

  • 接口响应增强:为接口添加“数据格式化”“脱敏”“缓存”等功能,无需修改接口原有逻辑。

  • Java IO 中的装饰器模式:如 BufferedInputStream(缓冲装饰)、DataInputStream(数据类型装饰),都是对基础 InputStream 的功能扩展。

4.2.3 实战代码(文件读写增强场景)

// 1. 抽象组件:文件读写接口(定义核心功能)
public interface FileReader {
    String read(String filePath);
}

// 2. 具体组件:基础文件读写实现(核心功能)
public class BasicFileReader implements FileReader {
    @Override
    public String read(String filePath) {
        // 模拟基础文件读取逻辑
        System.out.println("读取文件:" + filePath);
        return "文件内容:Hello World";
    }
}

// 3. 装饰器抽象类(实现抽象组件,持有目标对象)
public abstract class FileReaderDecorator implements FileReader {
    // 持有目标对象(被装饰的对象)
    protected FileReader fileReader;

    public FileReaderDecorator(FileReader fileReader) {
        this.fileReader = fileReader;
    }

    // 重写 read 方法,默认调用目标对象的方法,子类可扩展
    @Override
    public String read(String filePath) {
        return fileReader.read(filePath);
    }
}

// 4. 具体装饰器1:日志装饰器(添加日志功能)
public class LogFileReaderDecorator extends FileReaderDecorator {
    public LogFileReaderDecorator(FileReader fileReader) {
        super(fileReader);
    }

    @Override
    public String read(String filePath) {
        // 装饰逻辑:读取前记录日志
        System.out.println("日志装饰器:开始读取文件 " + filePath);
        // 调用目标对象的核心功能
        String content = super.read(filePath);
        // 装饰逻辑:读取后记录日志
        System.out.println("日志装饰器:文件读取完成,内容长度:" + content.length());
        return content;
    }
}

// 5. 具体装饰器2:加密装饰器(添加加密功能)
public class EncryptFileReaderDecorator extends FileReaderDecorator {
    public EncryptFileReaderDecorator(FileReader fileReader) {
        super(fileReader);
    }

    @Override
    public String read(String filePath) {
        // 先调用目标对象的核心功能
        String content = super.read(filePath);
        // 装饰逻辑:对内容进行加密(模拟加密)
        String encryptedContent = "加密后:" + content + "_encrypted";
        System.out.println("加密装饰器:文件内容加密完成");
        return encryptedContent;
    }
}

// 6. 实战使用(多层装饰,组合功能)
public class DecoratorTest {
    public static void main(String[] args) {
        // 1. 基础文件读取
        FileReader basicReader = new BasicFileReader();

        // 2. 添加日志装饰(基础功能+日志)
        FileReader logReader = new LogFileReaderDecorator(basicReader);

        // 3. 添加加密装饰(基础功能+日志+加密)
        FileReader encryptReader = new EncryptFileReaderDecorator(logReader);

        // 4. 调用装饰后的方法,触发所有装饰逻辑
        String content = encryptReader.read("D:/test.txt");
        System.out.println("最终读取结果:" + content);
    }
}

4.2.4 注意事项

  • 装饰器与目标对象必须实现同一接口:确保装饰器可以替代目标对象,符合里氏替换原则。

  • 支持多层装饰:装饰器可嵌套使用,实现功能的组合,但需避免装饰层数过多,导致代码复杂度提升。

  • 与继承区分:继承是静态扩展功能,装饰器是动态扩展功能,优先使用装饰器,避免继承带来的耦合。

4.3 适配器模式(Adapter Pattern)

4.3.1 设计思想与架构定位

核心思想:将一个类的接口转换成客户端期望的另一个接口,使原本接口不兼容的类可以一起工作,解决“接口不匹配”的架构问题。

架构定位:解决“模块接口不兼容”的问题,适用于第三方组件集成、旧系统改造、跨模块接口适配等场景,是系统集成的核心模式。

4.3.2 实战场景

  • 第三方组件集成:第三方接口的返回格式、方法名与本地系统不兼容,通过适配器转换接口,实现集成。

  • 旧系统改造:旧系统的接口与新系统接口不兼容,无需修改旧系统代码,通过适配器适配新接口。

  • SpringMVC 中的适配器模式:HandlerAdapter 适配不同类型的处理器(如 Controller、HttpRequestHandler),统一调用方式。

4.3.3 实战代码(第三方支付接口适配场景)

// 1. 客户端期望的接口(本地系统接口)
public interface PaymentAdapter {
    // 本地系统期望的支付方法:入参为金额,返回支付结果
    String pay(double amount);
}

// 2. 第三方支付接口(接口不兼容,方法名、入参不同)
public class ThirdPartyPayment {
    // 第三方接口方法:入参为金额字符串,返回布尔值
    public boolean doPayment(String amount) {
        System.out.println("第三方支付:金额 " + amount + " 元");
        return true; // 支付成功返回true
    }
}

// 3. 适配器类(将第三方接口适配为本地系统接口)
public class ThirdPartyPaymentAdapter implements PaymentAdapter {
    // 持有第三方接口对象
    private ThirdPartyPayment thirdPartyPayment;

    public ThirdPartyPaymentAdapter(ThirdPartyPayment thirdPartyPayment) {
        this.thirdPartyPayment = thirdPartyPayment;
    }

    // 适配本地接口方法,调用第三方接口
    @Override
    public String pay(double amount) {
        // 1. 转换入参:本地double类型 → 第三方String类型
        String amountStr = String.valueOf(amount);
        // 2. 调用第三方接口
        boolean result = thirdPartyPayment.doPayment(amountStr);
        // 3. 转换返回值:第三方布尔值 → 本地字符串
        return result ? "支付成功" : "支付失败";
    }
}

// 4. 实战使用(客户端调用本地接口,无需关注第三方接口细节)
public class AdapterTest {
    public static void main(String[] args) {
        // 第三方支付对象
        ThirdPartyPayment thirdPartyPayment = new ThirdPartyPayment();
        // 创建适配器
        PaymentAdapter adapter = new ThirdPartyPaymentAdapter(thirdPartyPayment);
        // 客户端调用本地接口,适配器自动适配第三方接口
        String result = adapter.pay(99.9);
        System.out.println("客户端收到结果:" + result);
    }
}

4.3.4 注意事项

  • 适配器模式的核心是“接口转换”,不修改原有接口和实现类,符合开放-封闭原则。

  • 避免过度适配:若接口差异过大,适配成本过高,建议重新设计接口,而非强行使用适配器。

  • 区分适配器与装饰器:适配器侧重“接口兼容”,装饰器侧重“功能扩展”,二者场景不同,不可混淆。

4.4 组合模式(Composite Pattern)

4.4.1 设计思想与架构定位

核心思想:将对象组合成树形结构,以表示“部分-整体”的层次关系,使得客户端对单个对象和组合对象的调用方式一致,无需区分处理。

架构定位:解决“树形结构数据的统一操作”问题,适用于具有层次结构的场景(如菜单、部门、文件目录),简化客户端对树形结构的操作。

4.4.2 实战场景

  • 系统菜单:菜单分为一级菜单、二级菜单、三级菜单,客户端需要统一遍历、展示所有菜单,无需区分单个菜单和菜单组。

  • 部门结构:公司部门分为总部、分公司、部门、小组,需要统一统计部门人数、查询部门信息。

  • 文件目录:文件夹和文件构成树形结构,客户端需要统一删除、复制、遍历所有文件和文件夹。

4.4.3 实战代码(系统菜单场景)

// 1. 抽象组件:菜单接口(定义统一操作)
public interface MenuComponent {
    // 展示菜单
    void show();
    // 添加子菜单(组合对象需要实现,单个对象无需实现)
    default void add(MenuComponent menu) {
        throw new UnsupportedOperationException("不支持添加子菜单");
    }
    // 删除子菜单(组合对象需要实现,单个对象无需实现)
    default void remove(MenuComponent menu) {
        throw new UnsupportedOperationException("不支持删除子菜单");
    }
    // 获取子菜单(组合对象需要实现,单个对象无需实现)
    default MenuComponent getChild(int index) {
        throw new UnsupportedOperationException("不支持获取子菜单");
    }
}

// 2. 叶子节点:单个菜单(无子女菜单)
public class LeafMenu implements MenuComponent {
    private String menuName;
    private String url;

    public LeafMenu(String menuName, String url) {
        this.menuName = menuName;
        this.url = url;
    }

    @Override
    public void show() {
        System.out.println("单个菜单:" + menuName + ",URL:" + url);
    }
}

// 3. 组合节点:菜单组(包含子菜单,可添加/删除子菜单)
public class CompositeMenu implements MenuComponent {
    private String menuName;
    // 存储子菜单(叶子节点或组合节点)
    private List<MenuComponent> children = new ArrayList<>();

    public CompositeMenu(String menuName) {
        this.menuName = menuName;
    }

    @Override
    public void show() {
        System.out.println("菜单组:" + menuName);
        // 递归展示所有子菜单
        for (MenuComponent child : children) {
            child.show();
        }
    }

    @Override
    public void add(MenuComponent menu) {
        children.add(menu);
    }

    @Override
    public void remove(MenuComponent menu) {
        children.remove(menu);
    }

    @Override
    public MenuComponent getChild(int index) {
        return children.get(index);
    }
}

// 4. 实战使用(客户端统一操作单个菜单和菜单组)
public class CompositeTest {
    public static void main(String[] args) {
        // 1. 创建叶子菜单(单个菜单)
        MenuComponent homeMenu = new LeafMenu("首页", "/home");
        MenuComponent userMenu = new LeafMenu("用户管理", "/user");

        // 2. 创建菜单组(系统管理),添加子菜单
        MenuComponent systemMenu = new CompositeMenu("系统管理");
        MenuComponent logMenu = new LeafMenu("日志管理", "/log");
        MenuComponent configMenu = new LeafMenu("配置管理", "/config");
        systemMenu.add(logMenu);
        systemMenu.add(configMenu);

        // 3. 创建根菜单组(总菜单),添加子菜单和菜单组
        MenuComponent rootMenu = new CompositeMenu("系统总菜单");
        rootMenu.add(homeMenu);
        rootMenu.add(userMenu);
        rootMenu.add(systemMenu);

        // 4. 客户端统一调用 show() 方法,无需区分单个菜单和菜单组
        rootMenu.show();
    }
}

4.4.4 注意事项

  • 抽象组件需定义统一接口:确保叶子节点和组合节点的操作方式一致,客户端无需区分处理。

  • 避免过度设计:若树形结构简单、无需频繁添加/删除子节点,无需使用组合模式,直接使用普通类即可。

  • 递归操作需注意性能:组合节点的递归操作(如 show())可能导致栈溢出,需控制树形结构的深度。

第五章 行为型模式:对象协作的标准化

行为型模式的核心架构定位:规范对象/模块间的通信与协作方式,解决对象间行为耦合过高、协作逻辑混乱的问题,使对象间的交互更灵活、可复用、可扩展。

核心设计思想:将对象的“行为”与“对象本身”分离,通过引入中间层(如观察者、中介者),规范对象间的交互,降低耦合。

5.1 观察者模式(Observer Pattern)

5.1.1 设计思想与架构定位

核心思想:定义“一对多”的依赖关系,当一个对象(被观察者)的状态发生变化时,所有依赖它的对象(观察者)都会收到通知,并自动更新自身状态,无需被观察者主动调用观察者方法。

架构定位:解决“对象状态变化后,多对象需同步响应”的问题,适用于事件通知、状态同步场景,实现“发布-订阅”的解耦,让被观察者与观察者完全分离,互不依赖。

5.1.2 实战场景

  • 系统通知机制:用户注册成功后,需要同步发送短信通知、邮件通知、积分赠送,这些操作作为观察者,监听用户注册(被观察者)的状态变化。

  • 配置中心:配置文件更新后,所有依赖该配置的服务(观察者)自动更新本地配置,无需重启服务。

  • Spring 中的应用:Spring 的 ApplicationEvent 和 ApplicationListener 接口,底层基于观察者模式,实现事件驱动编程(如上下文刷新事件、自定义业务事件)。

5.1.3 实战代码(SpringBoot 自定义事件场景)

// 1. 定义被观察者(事件对象):用户注册事件
public class UserRegisterEvent extends ApplicationEvent {
    // 事件携带的数据(用户ID)
    private String userId;

    // 构造方法,传入事件源和数据
    public UserRegisterEvent(Object source, String userId) {
        super(source);
        this.userId = userId;
    }

    // getter 方法
    public String getUserId() {
        return userId;
    }
}

// 2. 定义被观察者(事件发布者):用户服务(发布注册事件)
@Service
public class UserService {
    // 注入 Spring 事件发布器
    @Autowired
    private ApplicationEventPublisher eventPublisher;

    // 用户注册方法,发布事件
    public void register(String userId) {
        // 1. 核心业务逻辑:用户注册
        System.out.println("用户 " + userId + " 注册成功");
        // 2. 发布事件(被观察者状态变化,通知所有观察者)
        eventPublisher.publishEvent(new UserRegisterEvent(this, userId));
    }
}

// 3. 定义观察者1:短信通知观察者
@Component
public class SmsNotificationListener implements ApplicationListener<UserRegisterEvent> {
    // 监听事件,触发响应逻辑
    @Override
    public void onApplicationEvent(UserRegisterEvent event) {
        String userId = event.getUserId();
        System.out.println("给用户 " + userId + " 发送注册成功短信");
    }
}

// 4. 定义观察者2:邮件通知观察者
@Component
public class EmailNotificationListener implements ApplicationListener<UserRegisterEvent> {
    @Override
    public void onApplicationEvent(UserRegisterEvent event) {
        String userId = event.getUserId();
        System.out.println("给用户 " + userId + " 发送注册成功邮件");
    }
}

// 5. 定义观察者3:积分赠送观察者
@Component
public class PointListener implements ApplicationListener<UserRegisterEvent> {
    @Override
    public void onApplicationEvent(UserRegisterEvent event) {
        String userId = event.getUserId();
        System.out.println("给用户 " + userId + " 赠送100注册积分");
    }
}

// 6. 测试使用
@SpringBootTest
public class ObserverTest {
    @Autowired
    private UserService userService;

    @Test
    public void testRegister() {
        // 调用注册方法,触发事件发布,所有观察者同步响应
        userService.register("1001");
    }
}

5.1.4 注意事项

  • 避免观察者过多:若观察者数量过多,被观察者状态变化时,所有观察者都会被通知,会导致响应延迟,建议拆分事件,避免“一通知多”的过度耦合。

  • 区分同步与异步:Spring 默认是同步通知(观察者依次执行),若观察者逻辑耗时,需改为异步通知(添加 @Async 注解),避免阻塞被观察者。

  • 避免循环依赖:观察者与被观察者之间不可相互依赖,否则会导致循环调用,引发系统异常。

5.2 策略模式(Strategy Pattern)

5.2.1 设计思想与架构定位

核心思想:定义一系列算法,将每个算法封装为独立的策略类,使算法可独立于使用它的客户端而变化,客户端可根据场景动态选择不同的算法,无需修改客户端代码。

架构定位:解决“算法多样化、需要灵活切换”的问题,遵循开放-封闭原则,将算法的定义与使用分离,提升算法的可扩展性和可维护性,避免大量 if-else 分支判断。

5.2.2 实战场景

  • 支付方式选择:不同用户(普通用户、VIP用户、会员用户)对应不同的支付折扣算法,根据用户类型动态选择折扣策略。

  • 数据排序:系统需要支持多种排序算法(冒泡排序、快速排序、归并排序),根据数据量动态选择合适的排序策略。

  • Spring 中的应用:Spring 的 Resource 接口(不同资源类型的读取策略)、HandlerMapping(不同请求映射策略),均基于策略模式设计。

5.2.3 实战代码(支付折扣策略场景)

// 1. 抽象策略:折扣算法接口(定义统一算法规范)
public interface DiscountStrategy {
    // 计算折扣后金额
    double calculateDiscount(double originalAmount);
}

// 2. 具体策略1:普通用户策略(无折扣)
public class NormalUserDiscount implements DiscountStrategy {
    @Override
    public double calculateDiscount(double originalAmount) {
        System.out.println("普通用户,无折扣");
        return originalAmount;
    }
}

// 3. 具体策略2:VIP用户策略(9折)
public class VipUserDiscount implements DiscountStrategy {
    @Override
    public double calculateDiscount(double originalAmount) {
        System.out.println("VIP用户,9折优惠");
        return originalAmount * 0.9;
    }
}

// 4. 具体策略3:会员用户策略(8折)
public class MemberUserDiscount implements DiscountStrategy {
    @Override
    public double calculateDiscount(double originalAmount) {
        System.out.println("会员用户,8折优惠");
        return originalAmount * 0.8;
    }
}

// 5. 策略上下文:统一管理策略,供客户端调用
@Service
public class DiscountContext {
    // 存储所有策略(Spring 自动注入所有 DiscountStrategy 实现类)
    private final Map<String, DiscountStrategy> strategyMap;

    // 构造方法注入所有策略,key为策略名称(如normal、vip、member)
    public DiscountContext(Map<String, DiscountStrategy> strategyMap) {
        this.strategyMap = strategyMap;
    }

    // 客户端调用方法,根据用户类型选择策略
    public double getDiscountAmount(String userType, double originalAmount) {
        // 根据用户类型获取对应的策略,默认使用普通用户策略
        DiscountStrategy strategy = strategyMap.getOrDefault(userType, new NormalUserDiscount());
        // 执行策略,返回折扣后金额
        return strategy.calculateDiscount(originalAmount);
    }
}

// 6. 实战使用
@SpringBootTest
public class StrategyTest {
    @Autowired
    private DiscountContext discountContext;

    @Test
    public void testDiscount() {
        double originalAmount = 100.0;
        // 普通用户
        double normalAmount = discountContext.getDiscountAmount("normal", originalAmount);
        // VIP用户
        double vipAmount = discountContext.getDiscountAmount("vip", originalAmount);
        // 会员用户
        double memberAmount = discountContext.getDiscountAmount("member", originalAmount);

        System.out.println("普通用户实付:" + normalAmount);
        System.out.println("VIP用户实付:" + vipAmount);
        System.out.println("会员用户实付:" + memberAmount);
    }
}

5.2.4 注意事项

  • 策略类职责单一:每个策略类只实现一种算法,遵循单一职责原则,便于维护和扩展。

  • 策略上下文简化:上下文仅负责策略的选择和调用,不负责算法的实现,避免上下文过于复杂。

  • 避免策略过多:若策略数量过多(超过5个),建议结合工厂模式管理策略,提升代码可读性。

5.3 模板方法模式(Template Method Pattern)

5.3.1 设计思想与架构定位

核心思想:定义一个操作的核心流程(模板方法),将流程中的可变步骤延迟到子类中实现,子类可在不修改核心流程的前提下,重写可变步骤,实现流程的定制化。

架构定位:解决“流程固定、步骤可变”的问题,适用于具有统一流程但具体实现不同的场景(如业务审批、数据导入),统一流程规范,减少重复编码,提升代码复用性。

5.3.2 实战场景

  • 业务审批流程:所有审批(请假审批、报销审批、采购审批)的核心流程一致(提交→审核→审批→归档),但审核、审批的具体逻辑不同。

  • 数据导入流程:不同类型数据(用户数据、商品数据、订单数据)的导入流程一致(读取文件→校验数据→解析数据→入库),但校验、解析逻辑不同。

  • Spring 中的应用:Spring 的 JdbcTemplate(核心流程固定,查询、更新的具体逻辑由用户自定义),底层基于模板方法模式。

5.3.3 实战代码(数据导入场景)

// 1. 抽象模板类:定义数据导入的核心流程(模板方法)
public abstract class DataImportTemplate {
    /**
     * 模板方法:核心流程(固定不变)
     * 用 final 修饰,防止子类修改核心流程
     */
    public final void importData(String filePath) {
        // 步骤1:读取文件(固定步骤)
        String fileContent = readFile(filePath);
        // 步骤2:校验数据(可变步骤,子类实现)
        boolean validateResult = validateData(fileContent);
        if (!validateResult) {
            System.out.println("数据校验失败,导入终止");
            return;
        }
        // 步骤3:解析数据(可变步骤,子类实现)
        List<?> dataList = parseData(fileContent);
        // 步骤4:数据入库(固定步骤)
        saveData(dataList);
        // 步骤5:导入完成通知(固定步骤)
        notifyComplete();
    }

    // 固定步骤1:读取文件
    private String readFile(String filePath) {
        System.out.println("读取文件:" + filePath);
        // 模拟读取文件内容
        return "模拟文件内容:id,name,age";
    }

    // 可变步骤2:校验数据(抽象方法,子类实现)
    protected abstract boolean validateData(String fileContent);

    // 可变步骤3:解析数据(抽象方法,子类实现)
    protected abstract List<?> parseData(String fileContent);

    // 固定步骤4:数据入库
    private void saveData(List<?> dataList) {
        System.out.println("数据入库,共 " + dataList.size() + " 条数据");
    }

    // 固定步骤5:导入完成通知
    private void notifyComplete() {
        System.out.println("数据导入完成");
    }
}

// 2. 具体子类1:用户数据导入(实现可变步骤)
public class UserDataImport extends DataImportTemplate {
    @Override
    protected boolean validateData(String fileContent) {
        System.out.println("校验用户数据:检查是否包含id、name、age字段");
        // 模拟校验逻辑
        return fileContent.contains("id") && fileContent.contains("name") && fileContent.contains("age");
    }

    @Override
    protected List<?> parseData(String fileContent) {
        System.out.println("解析用户数据:将文件内容转换为User对象列表");
        // 模拟解析逻辑
        List<User&gt; userList = new ArrayList<>();
        userList.add(new User(1L, "张三", 25));
        return userList;
    }
}

// 3. 具体子类2:商品数据导入(实现可变步骤)
public class ProductDataImport extends DataImportTemplate {
    @Override
    protected boolean validateData(String fileContent) {
        System.out.println("校验商品数据:检查是否包含id、productName、price字段");
        // 模拟校验逻辑
        return fileContent.contains("id") && fileContent.contains("productName") && fileContent.contains("price");
    }

    @Override
    protected List<?> parseData(String fileContent) {
        System.out.println("解析商品数据:将文件内容转换为Product对象列表");
        // 模拟解析逻辑
        List<Product&gt; productList = new ArrayList<>();
        productList.add(new Product(100L, "手机", 2999.0));
        return productList;
    }
}

// 4. 实战使用
public class TemplateMethodTest {
    public static void main(String[] args) {
        // 导入用户数据
        DataImportTemplate userImport = new UserDataImport();
        userImport.importData("D:/user.csv");

        System.out.println("-------------------");

        // 导入商品数据
        DataImportTemplate productImport = new ProductDataImport();
        productImport.importData("D:/product.csv");
    }
}

// 辅助实体类
class User {
    private Long id;
    private String name;
    private Integer age;

    // 构造方法、getter/setter 省略
}

class Product {
    private Long id;
    private String productName;
    private Double price;

    // 构造方法、getter/setter 省略
}

5.3.4 注意事项

  • 模板方法用 final 修饰:核心流程必须固定,防止子类修改流程顺序,破坏流程规范。

  • 可变步骤用 protected 修饰:子类仅需重写可变步骤,无需关注固定步骤,降低子类实现成本。

  • 避免模板类过于复杂:若核心流程步骤过多,建议拆分模板类,遵循单一职责原则,避免一个模板类管理多个不相关的流程。

5.4 责任链模式(Chain of Responsibility Pattern)

5.4.1 设计思想与架构定位

核心思想:将多个处理者(Handler)组成一条责任链,当请求到来时,请求会沿着责任链依次传递,直到有一个处理者能够处理该请求,处理完成后终止传递;若所有处理者都无法处理,可设置默认处理逻辑。

架构定位:解决“请求需要多个处理者依次处理”的问题,适用于请求处理流程不确定、需要动态调整处理顺序的场景(如权限校验、请求过滤、异常处理),实现处理者的解耦,便于新增和删除处理者。

5.4.2 实战场景

  • 接口权限校验链:请求到来时,依次经过“登录校验→角色校验→接口权限校验→参数校验”,任意一步校验失败,直接返回错误。

  • 请求日志过滤链:请求经过“请求日志打印→敏感参数过滤→请求频率限制”,每一步处理完成后,传递给下一个处理者。

  • Spring 中的应用:SpringMVC 的拦截器链(HandlerInterceptor)、Spring Security 的过滤器链,均基于责任链模式设计。

5.4.3 实战代码(接口权限校验链场景)

// 1. 抽象处理者:权限校验接口
public interface PermissionHandler {
    // 设置下一个处理者(构建责任链)
    void setNextHandler(PermissionHandler nextHandler);
    // 处理请求(校验逻辑)
    boolean handleRequest(String userId, String interfaceId);
}

// 2. 具体处理者1:登录校验(第一个处理者)
public class LoginHandler implements PermissionHandler {
    private PermissionHandler nextHandler;

    @Override
    public void setNextHandler(PermissionHandler nextHandler) {
        this.nextHandler = nextHandler;
    }

    @Override
    public boolean handleRequest(String userId, String interfaceId) {
        System.out.println("执行登录校验:检查用户 " + userId + " 是否登录");
        // 模拟登录校验:userId不为空则登录成功
        if (userId == null || userId.isEmpty()) {
            System.out.println("登录校验失败:用户未登录");
            return false;
        }
        // 登录校验通过,传递给下一个处理者
        if (nextHandler != null) {
            return nextHandler.handleRequest(userId, interfaceId);
        }
        // 没有下一个处理者,校验通过
        return true;
    }
}

// 3. 具体处理者2:角色校验(第二个处理者)
public class RoleHandler implements PermissionHandler {
    private PermissionHandler nextHandler;

    @Override
    public void setNextHandler(PermissionHandler nextHandler) {
        this.nextHandler = nextHandler;
    }

    @Override
    public boolean handleRequest(String userId, String interfaceId) {
        System.out.println("执行角色校验:检查用户 " + userId + " 是否有对应角色");
        // 模拟角色校验:userId为1001的用户有角色权限
        if (!"1001".equals(userId)) {
            System.out.println("角色校验失败:用户无对应角色权限");
            return false;
        }
        // 角色校验通过,传递给下一个处理者
        if (nextHandler != null) {
            return nextHandler.handleRequest(userId, interfaceId);
        }
        return true;
    }
}

// 4. 具体处理者3:接口权限校验(第三个处理者)
public class InterfacePermissionHandler implements PermissionHandler {
    private PermissionHandler nextHandler;

    @Override
    public void setNextHandler(PermissionHandler nextHandler) {
        this.nextHandler = nextHandler;
    }

    @Override
    public boolean handleRequest(String userId, String interfaceId) {
        System.out.println("执行接口权限校验:检查用户 " + userId + " 是否有权访问接口 " + interfaceId);
        // 模拟接口权限校验:接口id为"api/user"的接口允许访问
        if (!"api/user".equals(interfaceId)) {
            System.out.println("接口权限校验失败:用户无权访问该接口");
            return false;
        }
        // 接口权限校验通过,传递给下一个处理者(若有)
        if (nextHandler != null) {
            return nextHandler.handleRequest(userId, interfaceId);
        }
        return true;
    }
}

// 5. 责任链构建者(简化责任链构建)
public class PermissionHandlerChainBuilder {
    // 存储所有处理者
    private List<PermissionHandler> handlers = new ArrayList<>();

    // 添加处理者
    public PermissionHandlerChainBuilder addHandler(PermissionHandler handler) {
        handlers.add(handler);
        return this;
    }

    // 构建责任链(依次设置下一个处理者)
    public PermissionHandler build() {
        for (int i = 0; i < handlers.size() - 1; i++) {
            handlers.get(i).setNextHandler(handlers.get(i + 1));
        }
        // 返回第一个处理者(责任链入口)
        return handlers.get(0);
    }
}

// 6. 实战使用
public class ChainOfResponsibilityTest {
    public static void main(String[] args) {
        // 1. 构建责任链
        PermissionHandler chain = new PermissionHandlerChainBuilder()
                .addHandler(new LoginHandler())
                .addHandler(new RoleHandler())
                .addHandler(new InterfacePermissionHandler())
                .build();

        // 2. 测试1:所有校验通过
        System.out.println("=== 测试1:所有校验通过 ===");
        boolean result1 = chain.handleRequest("1001", "api/user");
        System.out.println("校验结果:" + (result1 ? "通过" : "失败"));

        // 3. 测试2:角色校验失败
        System.out.println("\n=== 测试2:角色校验失败 ===");
        boolean result2 = chain.handleRequest("1002", "api/user");
        System.out.println("校验结果:" + (result2 ? "通过" : "失败"));

        // 4. 测试3:接口权限校验失败
        System.out.println("\n=== 测试3:接口权限校验失败 ===");
        boolean result3 = chain.handleRequest("1001", "api/order");
        System.out.println("校验结果:" + (result3 ? "通过" : "失败"));
    }
}

5.4.4 注意事项

  • 责任链顺序:处理者的顺序至关重要,需根据业务逻辑合理排列(如先登录校验,再角色校验),避免顺序错误导致校验逻辑异常。

  • 避免责任链过长:若处理者过多,会导致请求处理效率降低,建议拆分责任链,按功能模块划分多个小链条。

  • 设置默认处理:若所有处理者都无法处理请求,需设置默认处理逻辑,避免请求“无响应”。

5.5 中介者模式(Mediator Pattern)

5.5.1 设计思想与架构定位

核心思想:定义一个中介者对象,封装多个对象(同事对象)之间的交互逻辑,同事对象不再直接相互通信,而是通过中介者进行通信,降低同事对象之间的耦合。

架构定位:解决“多个对象相互依赖、通信复杂”的问题,适用于对象数量多、交互频繁的场景(如系统组件通信、团队协作管理),将“多对多”的复杂依赖,转化为“一对多”的简单依赖,提升系统的可维护性。

5.5.2 实战场景

  • 系统组件通信:系统中的订单组件、库存组件、支付组件、物流组件,不再直接相互调用,而是通过中介者(订单中介者)协调通信,实现订单创建、库存扣减、支付、物流的协同。

  • 聊天室功能:多个用户(同事对象)之间发送消息,不再直接点对点发送,而是通过聊天室(中介者)转发消息,简化用户间的通信逻辑。

  • Spring 中的应用:Spring 容器作为中介者,管理所有 Bean 之间的依赖关系,Bean 之间通过容器进行依赖注入和通信,无需直接关联。

5.5.3 实战代码(订单协同场景)

// 1. 抽象中介者:定义中介者的核心方法(协调同事对象)
public interface OrderMediator {
    // 注册同事对象
    void registerColleague(Colleague colleague);
    // 转发消息(协调同事对象交互)
    void relayMessage(Colleague sender, String message);
}

// 2. 抽象同事对象:定义同事对象的通用方法
public abstract class Colleague {
    // 持有中介者对象
    protected OrderMediator mediator;

    public Colleague(OrderMediator mediator) {
        this.mediator = mediator;
        // 注册到中介者
        mediator.registerColleague(this);
    }

    // 发送消息(通过中介者转发)
    public abstract void sendMessage(String message);
    // 接收消息(处理中介者转发的消息)
    public abstract void receiveMessage(String message);
}

// 3. 具体同事1:订单组件
public class OrderColleague extends Colleague {
    public OrderColleague(OrderMediator mediator) {
        super(mediator);
    }

    @Override
    public void sendMessage(String message) {
        System.out.println("订单组件发送消息:" + message);
        // 通过中介者转发消息
        mediator.relayMessage(this, message);
    }

    @Override
    public void receiveMessage(String message) {
        System.out.println("订单组件接收消息:" + message);
        // 处理消息(如订单状态更新)
        if ("库存扣减成功".equals(message)) {
            System.out.println("订单组件:更新订单状态为「已付款」");
        } else if ("物流已发货".equals(message)) {
            System.out.println("订单组件:更新订单状态为「已发货」");
        }
    }

    // 订单创建方法(触发协同流程)
    public void createOrder(Long orderId) {
        System.out.println("订单组件:创建订单 " + orderId);
        // 发送消息,请求库存扣减
        sendMessage("请求扣减订单 " + orderId + " 的库存");
    }
}

// 4. 具体同事2:库存组件
public class StockColleague extends Colleague {
    public StockColleague(OrderMediator mediator) {
        super(mediator);
    }

    @Override
    public void sendMessage(String message) {
        System.out.println("库存组件发送消息:" + message);
        mediator.relayMessage(this, message);
    }

    @Override
    public void receiveMessage(String message) {
        System.out.println("库存组件接收消息:" + message);
        // 处理消息(扣减库存)
        if (message.startsWith("请求扣减订单")) {
            System.out.println("库存组件:扣减库存成功");
            // 发送消息,通知支付组件进行支付
            sendMessage("库存扣减成功,请进行支付");
        }
    }
}

// 5. 具体同事3:支付组件
public class PaymentColleague extends Colleague {
    public PaymentColleague(OrderMediator mediator) {
        super(mediator);
    }

    @Override
    public void sendMessage(String message) {
        System.out.println("支付组件发送消息:" + message);
        mediator.relayMessage(this, message);
    }

    @Override
    public void receiveMessage(String message) {
        System.out.println("支付组件接收消息:" + message);
        // 处理消息(进行支付)
        if ("库存扣减成功,请进行支付".equals(message)) {
            System.out.println("支付组件:支付成功");
            // 发送消息,通知物流组件发货
            sendMessage("支付成功,请安排物流发货");
        }
    }
}

// 6. 具体同事4:物流组件
public class LogisticsColleague extends Colleague {
    public LogisticsColleague(OrderMediator mediator) {
        super(mediator);
    }

    @Override
    public void sendMessage(String message) {
        System.out.println("物流组件发送消息:" + message);
        mediator.relayMessage(this, message);
    }

    @Override
    public void receiveMessage(String message) {
        System.out.println("物流组件接收消息:" + message);
        // 处理消息(安排发货)
        if ("支付成功,请安排物流发货".equals(message)) {
            System.out.println("物流组件:安排发货");
            // 发送消息,通知订单组件更新状态
            sendMessage("物流已发货");
        }
    }
}

// 7. 具体中介者:订单中介者(实现协调逻辑)
public class OrderMediatorImpl implements OrderMediator {
    // 存储所有同事对象
    private List<Colleague> colleagues = new ArrayList<>();

    @Override
    public void registerColleague(Colleague colleague) {
        colleagues.add(colleague);
    }

    @Override
    public void relayMessage(Colleague sender, String message) {
        // 转发消息给所有非发送者的同事对象(可根据业务调整转发范围)
        for (Colleague colleague : colleagues) {
            if (!colleague.equals(sender)) {
                colleague.receiveMessage(message);
            }
        }
    }
}

// 8. 实战使用
public class MediatorTest {
    public static void main(String[] args) {
        // 1. 创建中介者
        OrderMediator mediator = new OrderMediatorImpl();

        // 2. 创建同事对象(自动注册到中介者)
        OrderColleague orderColleague = new OrderColleague(mediator);
        StockColleague stockColleague = new StockColleague(mediator);
        PaymentColleague paymentColleague = new PaymentColleague(mediator);
        LogisticsColleague logisticsColleague = new LogisticsColleague(mediator);

        // 3. 触发订单创建,启动协同流程
        System.out.println("=== 启动订单协同流程 ===");
        orderColleague.createOrder(1001L);
    }
}

5.5.4 注意事项

  • 中介者职责单一:中介者仅负责协调同事对象的通信,不负责具体的业务逻辑,避免中介者过于庞大(避免“中介者臃肿”问题)。

  • 避免过度依赖中介者:同事对象之间的简单通信,无需通过中介者,仅在多对象复杂交互时使用,避免过度设计。

  • 同事对象解耦:同事对象仅依赖中介者,不依赖其他同事对象,便于单独修改和扩展。

第六章 设计模式选型与架构实践

前面章节详细讲解了常用设计模式的理论与实战,本章聚焦“架构选型”核心,结合不同业务场景,给出设计模式的选型建议、组合使用技巧,以及企业级项目中的落地注意事项,帮助开发者将设计模式真正融入架构设计,而非单纯的代码技巧。

6.1 设计模式选型核心原则

设计模式的选型没有“最优解”,只有“最适配”,核心遵循以下4个原则,避免为了使用模式而使用模式(过度设计):

  1. 贴合业务场景:选型前先明确业务需求和架构痛点,比如“对象创建复杂”用创建型模式,“模块交互复杂”用行为型模式,“接口不兼容”用适配器模式。

  2. 遵循设计原则:选型必须符合面向对象七大设计原则,优先保证架构的高内聚、低耦合,避免选型后导致代码冗余、耦合升高。

  3. 兼顾可维护性:选择团队熟悉、易于理解的模式,避免使用过于复杂的模式(如抽象工厂模式、备忘录模式),除非业务场景确实需要。

  4. 避免过度设计:简单场景无需使用复杂模式,比如“只有一个支付方式”无需使用工厂模式,直接使用普通类即可;“对象属性少”无需使用建造者模式。

6.2 常见业务场景与模式选型对应表

业务场景推荐设计模式选型说明
全局工具类、连接池、配置类单例模式确保全局唯一实例,减少资源浪费,贴合无状态对象的复用需求
多种支付方式、第三方组件切换工厂方法模式、策略模式工厂模式负责对象创建,策略模式负责算法/逻辑切换,组合使用更灵活
复杂对象创建(多属性、可配置)建造者模式拆解创建步骤,支持灵活配置对象属性,提升代码可读性
日志、权限、缓存等横切逻辑代理模式(Spring AOP)不修改原有代码,统一添加横切逻辑,符合开放-封闭原则
第三方接口集成、旧系统改造适配器模式解决接口不兼容问题,无需修改原有代码,快速实现集成
菜单、部门、文件目录等树形结构组合模式统一操作单个对象和组合对象,简化树形结构的遍历和管理
事件通知、状态同步(如用户注册后多操作)观察者模式实现发布-订阅解耦,被观察者状态变化时,所有观察者同步响应
流程固定、步骤可变(如数据导入、审批)模板方法模式统一流程规范,子类重写可变步骤,提升代码复用性
多处理者依次处理请求(如权限校验链)责任链模式动态调整处理顺序,新增/删除处理者无需修改原有代码
多对象复杂交互(如订单、库存、支付协同)中介者模式降低对象间的耦合,通过中介者协调通信,简化交互逻辑

6.3 设计模式组合使用技巧

实际架构设计中,单一模式往往无法满足复杂业务需求,需要多种模式组合使用,以下是最常用的4种组合方式,结合实战场景说明:

6.3.1 组合1:工厂模式 + 策略模式(最常用)

场景:多种支付方式(微信、支付宝、银联),需要灵活切换支付方式,同时统一创建支付对象。

组合逻辑:工厂模式负责创建不同类型的支付对象(微信支付、支付宝支付),策略模式负责定义不同支付方式的支付算法,客户端通过工厂获取支付对象,通过策略模式切换支付逻辑,二者结合实现“创建+切换”的双重灵活。

核心优势:新增支付方式时,只需新增工厂类和策略类,无需修改客户端代码,完全符合开放-封闭原则。

6.3.2 组合2:代理模式 + 单例模式

场景:全局日志工具类(单例),需要在日志输出前后添加耗时统计、参数打印逻辑。

组合逻辑:单例模式确保日志工具类全局唯一,代理模式为日志工具类创建代理对象,在代理对象中添加耗时统计、参数打印等横切逻辑,既保证资源复用,又实现功能扩展。

核心优势:不修改单例类的原有代码,通过代理扩展功能,避免单例类职责过重。

6.3.3 组合3:模板方法模式 + 策略模式

场景:数据导入流程(固定流程:读取→校验→解析→入库),不同数据类型的校验、解析逻辑不同,且解析算法可灵活切换。

组合逻辑:模板方法模式定义数据导入的固定流程,策略模式定义不同的解析算法(如JSON解析、CSV解析),将解析步骤作为策略,子类可根据数据类型选择不同的解析策略,实现流程与算法的解耦。

核心优势:既统一流程规范,又支持算法灵活切换,提升代码的可扩展性。

6.3.4 组合4:观察者模式 + 中介者模式

场景:系统事件通知(如订单状态变更),多个组件(订单、库存、物流、消息)需要同步响应,且组件间交互复杂。

组合逻辑:中介者模式协调多个组件的通信,观察者模式实现事件的发布与订阅,订单组件作为被观察者发布状态变更事件,中介者作为观察者接收事件,再协调其他组件(库存、物流)响应,避免组件间直接耦合。

核心优势:解决多组件复杂交互的问题,同时实现事件的同步响应,提升架构的可维护性。

6.4 企业级落地注意事项

  • 避免过度设计:这是最核心的注意事项,不要为了使用设计模式而强行套用,比如简单的对象创建无需使用工厂模式,简单的接口扩展无需使用装饰器模式。

  • 结合框架落地:充分利用 Spring 生态的特性,简化模式实现,比如用 Spring 的依赖注入管理工厂、策略对象,用 Spring AOP 实现代理模式,减少手动编码。

  • 团队统一规范:明确项目中设计模式的使用规范,避免同一功能使用不同的模式,导致代码风格混乱(如统一用双重检查锁实现单例,统一用策略模式处理算法切换)。

  • 注重代码可读性:设计模式的实现要简洁明了,添加必要的注释,说明模式的使用目的和核心逻辑,避免其他开发者难以理解。

  • 适配架构迭代:设计模式的选型要考虑架构的长期迭代,比如选择工厂模式、策略模式,便于后续新增功能;避免选择耦合度高的模式(如桥接模式),增加迭代成本。

第七章 总结与拓展

7.1 核心总结

设计模式的本质是“面向对象设计原则的落地”,其核心价值是“解耦、复用、可维护”,而非单纯的代码技巧。本手册从架构视角出发,梳理了创建型、结构型、行为型三大类常用设计模式,每一种模式都围绕“解决特定架构问题”展开,结合企业级实战场景,提供了可直接复用的代码方案和选型建议。

核心要点回顾:

  • 创建型模式:聚焦“对象创建”,解决创建逻辑与业务逻辑的耦合,核心是“面向抽象创建对象”。

  • 结构型模式:聚焦“模块组合”,优化系统结构,解决模块间耦合过高的问题,核心是“组合复用”。

  • 行为型模式:聚焦“对象协作”,规范对象间的通信方式,解决交互复杂的问题,核心是“行为解耦”。

  • 选型核心:贴合业务场景、遵循设计原则、避免过度设计,优先选择简单、易维护的模式。

7.2 拓展方向

设计模式的学习是一个“理论→实践→总结”的循环过程,后续可从以下3个方向进一步拓展,深化对设计模式的理解和应用:

  1. 源码中的设计模式:阅读 Spring、MyBatis、OkHttp 等主流框架的源码,分析框架中设计模式的应用(如 Spring 中的单例、代理、观察者模式),理解模式在大型架构中的落地思路。

  2. 复杂场景的模式组合:针对更复杂的业务场景(如微服务架构、分布式系统),探索多种设计模式的组合使用(如工厂+策略+代理),提升架构设计能力。

  3. 设计模式与架构模式的结合:将设计模式与微服务架构、DDD(领域驱动设计)、事件驱动架构等结合,比如用聚合模式(组合模式)实现 DDD 中的聚合根,用观察者模式实现事件驱动。

7.3 寄语

设计模式不是“银弹”,无法解决所有架构问题,但它是开发者提升架构设计能力的重要工具。学习设计模式的关键,不是死记硬背代码,而是理解其设计思想和解决的架构痛点,在实际项目中灵活运用,平衡“代码质量”与“开发效率”,打造高可用、可维护、可扩展的系统架构。