Java 设计模式(一)

共 16992字,需浏览 34分钟

 ·

2026-01-26 15:18


本文聚焦 3 种高频行为型设计模式,结合支付、工作流等真实业务场景,从核心原理、代码实现、优化技巧三方面拆解,助力开发者在项目中规避硬编码、提升代码可扩展性,同时适配面试高频考点。

1. 策略模式

1.1 核心定义

策略模式(Strategy Pattern)属于行为型设计模式,核心思想是将可变的算法/业务规则封装为独立的策略类,使策略类之间可相互替换,同时让算法的变化独立于使用算法的客户端。其核心价值在于消除大量 if-else/switch 硬编码逻辑,提升代码的可维护性和扩展性。

1.2 问题场景(优化前)

开发支付接口时,若需支持支付宝、微信支付等多种方式,直接在控制器中通过分支判断选择支付方式,会存在明显弊端:新增支付渠道(如银联支付)需修改控制器代码,违反“开闭原则”;大量注入支付 Bean 导致代码冗余,分支逻辑复杂后难以调试。

@RestController
public class PayController {

    @Autowired
    private ALiPay aLiPay;

    @Autowired
    private WXPay wxPay;

    @RequestMapping("/pay")
    public Boolean pay(int payment, int amount) {
       // 硬编码分支,新增支付方式需修改此处
       return switch (payment) {
            case 1 -> aLiPay.pay(amount);
            case 2 -> wxPay.pay(amount);
            default -> false;
        };
    }

}

1.3 策略模式实现(优化后)

采用“接口定义 + 策略实现 + 工厂管理”三层结构,实现支付方式的灵活扩展。

步骤 1:定义策略接口

统一支付策略的行为规范,包含支付方法和支付类型获取方法,所有支付工具需实现此接口。

// 支付类型枚举,替代硬编码数字,提升可读性
public enum Payment {
    ALIPAY(1, "支付宝"),
    WXPAY(2, "微信支付"),
    UNIONPAY(3, "银联支付");

    private final int value;
    private final String desc;

    Payment(int value, String desc) {
        this.value = value;
        this.desc = desc;
    }

    public int getValue() {
        return value;
    }

    public String getDesc() {
        return desc;
    }
}

// 支付策略接口
public interface PayStrategy {
   // 支付核心方法
   Boolean pay(int amount);
   // 获取支付类型(关联枚举)
   Payment getType();
}

步骤 2:实现具体策略

针对不同支付方式,实现策略接口,封装各自的支付逻辑,通过 Spring 注解交给容器管理。

// 支付宝支付实现
@Component
public class ALiPayStrategy implements PayStrategy {
    @Override
    public Boolean pay(int amount) {
       // 封装支付宝支付真实逻辑(如调用第三方接口)
       System.out.println("支付宝支付:" + amount + "元");
        return true;
    }

    @Override
    public Payment getType() {
        return Payment.ALIPAY;
    }
}

// 微信支付实现
@Component
public class WXPayStrategy implements PayStrategy {
    @Override
    public Boolean pay(int amount) {
       // 封装微信支付真实逻辑
       System.out.println("微信支付:" + amount + "元");
        return true;
    }

    @Override
    public Payment getType() {
        return Payment.WXPAY;
    }
}

步骤 3:创建策略工厂

通过工厂类管理所有策略实例,利用 ConcurrentHashMap 存储“支付类型值 - 策略实例”映射,在初始化时自动扫描所有 PayStrategy 实现类并注入映射,避免客户端直接依赖策略类。

@Component
public class PayStrategyFactory {

   // 线程安全的Map存储策略映射
   private final ConcurrentHashMap<Integer, PayStrategy> paymentMap = new ConcurrentHashMap<>();

   // 构造器注入所有PayStrategy实现类,自动初始化映射
   @Autowired
    public PayStrategyFactory(List<PayStrategy> strategies) {
        for (PayStrategy strategy : strategies) {
            paymentMap.put(strategy.getType().getValue(), strategy);
        }
    }

   // 对外提供获取策略的方法,增加空值校验
   public PayStrategy getPayStrategy(int paymentType) {
        PayStrategy strategy = paymentMap.get(paymentType);
        if (strategy == null) {
            throw new IllegalArgumentException("不支持的支付方式:" + paymentType);
        }
        return strategy;
    }
}

步骤 4:客户端调用

控制器仅依赖策略工厂,通过支付类型获取对应策略并调用方法,无任何分支逻辑,新增支付方式无需修改此处代码。

@RestController
@RequestMapping("/pay")
public class PayController {

    @Autowired
    private PayStrategyFactory payStrategyFactory;

    @GetMapping
    public Boolean pay(@RequestParam int paymentType, @RequestParam int amount) {
       // 获取策略并执行支付
       PayStrategy strategy = payStrategyFactory.getPayStrategy(paymentType);
        return strategy.pay(amount);
    }
}

1.4 优势与扩展

优势:新增支付方式(如银联支付)仅需新增策略实现类,无需修改工厂和控制器,完全符合“开闭原则”;消除分支逻辑,代码简洁易维护。扩展:可结合注解简化策略注册,或通过配置中心动态加载策略。

2. 责任链模式

2.1 核心定义

责任链模式(Chain of Responsibility Pattern)是行为型设计模式,核心是将多个处理器(Handler)串联成一条链,请求沿链传递,直到某个处理器能处理该请求(或链尾结束)。每个处理器仅需关注自己负责的逻辑,实现请求发送者与处理者的解耦,适用于工作流审批、请求过滤等场景。

2.2 基础实现(单一 DTO 场景)

步骤 1:定义处理器抽象类

封装链的节点关系(下一个处理器)和请求传递逻辑,提供抽象处理方法供子类实现。

// 统一请求DTO
public class ReqDto {
   // 业务参数(示例:审批ID、请求内容)
   private Long id;
    private String content;
   // getter/setter
}

// 处理器抽象类
public abstract class AbstractHandler {
   // 下一个处理器
   protected AbstractHandler nextHandler;

   // 设置下一个处理器
   public void setNextHandler(AbstractHandler nextHandler) {
        this.nextHandler = nextHandler;
    }

   // 抽象处理方法(子类必须实现)
   public abstract Boolean handle(ReqDto reqDto);

   // 传递请求到下一个处理器
   protected Boolean passToNext(ReqDto reqDto) {
        if (nextHandler != null) {
            return nextHandler.handle(reqDto);
        }
       // 链尾无处理器,返回默认结果
       return true;
    }
}

步骤 2:实现具体处理器

每个处理器实现自己的业务逻辑,处理完成后决定是否传递给下一个处理器,通过 @Order 注解指定链的顺序。

// 处理器1:参数校验
@Component
@Order(1) // 顺序1,最先执行
public class ParamCheckHandler extends AbstractHandler {
    @Override
    public Boolean handle(ReqDto reqDto) {
        System.out.println("处理器1:执行参数校验");
       // 业务逻辑:校验参数非空
       if (reqDto.getId() == null || StringUtils.isEmpty(reqDto.getContent())) {
            System.out.println("参数校验失败,终止流程");
            return false;
        }
       // 校验通过,传递给下一个处理器
       return passToNext(reqDto);
    }
}

// 处理器2:权限验证
@Component
@Order(2) // 顺序2,次之执行
public class AuthHandler extends AbstractHandler {
    @Override
    public Boolean handle(ReqDto reqDto) {
        System.out.println("处理器2:执行权限验证");
       // 业务逻辑:模拟权限校验通过
       boolean hasAuth = true;
        if (!hasAuth) {
            System.out.println("权限不足,终止流程");
            return false;
        }
       // 权限通过,传递给下一个处理器
       return passToNext(reqDto);
    }
}

步骤 3:创建责任链工厂

通过工厂类组装处理器链,利用 Spring 自动注入所有 AbstractHandler 实现类,按 @Order 注解排序后串联成链,对外暴露执行入口。

@Component
public class WorkFlowFactory {
   // 链的第一个处理器
   private AbstractHandler firstHandler;

   // 注入所有处理器,按Order排序并组装链
   @Autowired
    public WorkFlowFactory(List<AbstractHandler> handlers) {
       // 按@Order注解值升序排序
       List<AbstractHandler> sortedHandlers = handlers.stream()
                .sorted(Comparator.comparingInt(handler -> {
                    Order order = handler.getClass().getAnnotation(Order.class);
                    return order != null ? order.value() : Integer.MAX_VALUE;
                }))
                .collect(Collectors.toList());

       // 组装责任链
       for (int i = 0; i < sortedHandlers.size() - 1; i++) {
            AbstractHandler current = sortedHandlers.get(i);
            AbstractHandler next = sortedHandlers.get(i + 1);
            current.setNextHandler(next);
        }
       // 赋值第一个处理器
       this.firstHandler = sortedHandlers.isEmpty() ? null : sortedHandlers.get(0);
    }

   // 对外提供执行工作流的方法
   public Boolean executeWorkFlow(ReqDto reqDto) {
        if (firstHandler == null) {
            throw new IllegalStateException("责任链未初始化,无处理器可用");
        }
        return firstHandler.handle(reqDto);
    }
}

步骤 4:客户端调用

@RestController
@RequestMapping("/workflow")
public class WorkFlowController {

    @Autowired
    private WorkFlowFactory workFlowFactory;

    @PostMapping("/execute")
    public Boolean execute(@RequestBody ReqDto reqDto) {
        return workFlowFactory.executeWorkFlow(reqDto);
    }
}

2.3 优化:泛型适配多 DTO 场景

基础实现仅支持单一 ReqDto,若需处理不同类型的请求(如 OrderReqDto、UserReqDto),可通过泛型改造,让责任链支持多种 DTO,无需重复编写处理器抽象类。

步骤 1:泛型处理器抽象类

// 泛型处理器抽象类,T为请求DTO类型
public abstract class GenericHandler<T> {
    protected GenericHandler<T> nextHandler;

    public void setNextHandler(GenericHandler<T> nextHandler) {
        this.nextHandler = nextHandler;
    }

    public abstract Boolean handle(T reqDto);

    protected Boolean passToNext(T reqDto) {
        if (nextHandler != null) {
            return nextHandler.handle(reqDto);
        }
        return true;
    }
}

步骤 2:按 DTO 类型创建专属处理器

// 订单DTO专属处理器1:订单状态校验
@Component
@Order(1)
public class OrderStatusHandler extends GenericHandler<OrderReqDto> {
    @Override
    public Boolean handle(OrderReqDto reqDto) {
        System.out.println("订单处理器1:校验订单状态");
       // 订单专属逻辑
       if (reqDto.getStatus() != 0) {
            System.out.println("订单状态异常,终止流程");
            return false;
        }
        return passToNext(reqDto);
    }
}

// 用户DTO专属处理器1:用户状态校验
@Component
@Order(1)
public class UserStatusHandler extends GenericHandler<UserReqDto> {
    @Override
    public Boolean handle(UserReqDto reqDto) {
        System.out.println("用户处理器1:校验用户状态");
       // 用户专属逻辑
       if (!reqDto.isEnabled()) {
            System.out.println("用户已禁用,终止流程");
            return false;
        }
        return passToNext(reqDto);
    }
}

步骤 3:泛型工厂 + 配置类管理多链

通过配置类为不同 DTO 类型创建专属责任链工厂,避免工厂类耦合。

// 订单责任链工厂
@Component
public class OrderWorkFlowFactory {
    private GenericHandler<OrderReqDto> firstHandler;

    @Autowired
    public OrderWorkFlowFactory(List<GenericHandler<OrderReqDto>> handlers) {
       // 排序并组装链(逻辑同基础工厂,略)
       List<GenericHandler<OrderReqDto>> sortedHandlers = handlers.stream()
                .sorted(Comparator.comparingInt(h -> {
                    Order order = h.getClass().getAnnotation(Order.class);
                    return order != null ? order.value() : Integer.MAX_VALUE;
                }))
                .collect(Collectors.toList());
       // 组装逻辑(略)
       this.firstHandler = sortedHandlers.isEmpty() ? null : sortedHandlers.get(0);
    }

    public Boolean execute(OrderReqDto reqDto) {
        if (firstHandler == null) {
            throw new IllegalStateException("订单责任链未初始化");
        }
        return firstHandler.handle(reqDto);
    }
}

// 用户责任链工厂(同理,略)
@Component
public class UserWorkFlowFactory {
   // 实现逻辑同OrderWorkFlowFactory,处理UserReqDto
}

2.4 核心优势

解耦请求发送者与处理者,新增处理器仅需实现抽象类并加入链,无需修改原有代码;每个处理器职责单一,符合“单一职责原则”;可动态调整处理器顺序或增减处理器,灵活性高。

3. 单例模式

3.1 核心定义

单例模式(Singleton Pattern)是创建型设计模式,核心是确保一个类在整个应用中仅有一个实例,并提供一个全局访问点。适用于工具类、配置类、线程池等场景,避免重复创建实例造成资源浪费。

3.2 核心原则

•          私有构造器:禁止外部通过 new 关键字创建实例;

•          全局访问点:提供静态方法获取唯一实例;

•          线程安全:多线程环境下确保实例唯一,避免创建多个实例;

•          延迟加载(可选):按需创建实例,减少启动时资源占用。

3.3 常见实现方式

3.3.1 饿汉式(立即加载)

类加载时直接创建实例,线程安全(依赖类加载机制),但无延迟加载特性,若实例初始化耗时久,会影响应用启动速度。

public class HungrySingleton {
   // 类加载时初始化实例,volatile可省略(类加载是原子操作)
   private static final HungrySingleton instance = new HungrySingleton();

   // 私有构造器,禁止外部实例化
   private HungrySingleton() {
       // 防止反射破坏单例(可选)
       if (instance != null) {
            throw new IllegalStateException("单例实例已存在,禁止重复创建");
        }
    }

   // 全局访问点
   public static HungrySingleton getInstance() {
        return instance;
    }
}

3.3.2 懒汉式(双重检查锁,推荐)

按需创建实例,通过双重检查锁(DCL)和 volatile 关键字保证线程安全,兼顾延迟加载和性能,是实际开发中最常用的实现方式。

public class LazySingleton {
   // volatile关键字:禁止指令重排,确保实例初始化完成后再被访问
   private static volatile LazySingleton instance;

   // 私有构造器
   private LazySingleton() {
       // 防止反射破坏单例
       if (instance != null) {
            throw new IllegalStateException("单例实例已存在,禁止重复创建");
        }
    }

   // 双重检查锁获取实例
   public static LazySingleton getInstance() {
       // 第一次检查:无锁,提升性能(避免每次都进入同步块)
       if (instance == null) {
           // 同步块:保证多线程下只有一个线程进入初始化逻辑
           synchronized (LazySingleton.class) {
               // 第二次检查:防止多个线程等待同步块后重复创建实例
               if (instance == null) {
                    instance = new LazySingleton();
                }
            }
        }
        return instance;
    }
}

关键说明:volatile 不可省略,因为 instance = new LazySingleton() 并非原子操作,会分为“分配内存、初始化实例、赋值给变量”三步,指令重排可能导致线程获取到未初始化的实例。

3.3.3 静态内部类式(推荐)

结合饿汉式和懒汉式优势,利用静态内部类的类加载机制实现延迟加载和线程安全,代码简洁,无需手动处理同步。

public class StaticInnerClassSingleton {
   // 私有构造器
   private StaticInnerClassSingleton() {
        if (InnerClass.instance != null) {
            throw new IllegalStateException("单例实例已存在,禁止重复创建");
        }
    }

   // 静态内部类:类加载时不会初始化,仅当调用getInstance时才加载
   private static class InnerClass {
        private static final StaticInnerClassSingleton instance = new StaticInnerClassSingleton();
    }

   // 全局访问点
   public static StaticInnerClassSingleton getInstance() {
        return InnerClass.instance;
    }
}

3.4 单例模式的破坏与防护

1. 反射破坏及防护

通过反射调用私有构造器可创建新实例,防护方式:在私有构造器中检查实例是否已存在,若存在则抛出异常(如上述实现中的逻辑)。

2. 序列化破坏及防护

若单例类实现 Serializable 接口,反序列化时会创建新实例,防护方式:重写 readResolve() 方法,返回已存在的单例实例。

public class LazySingleton implements Serializable {
   // 其他代码不变(略)

    // 反序列化时返回单例实例
   private Object readResolve() {
        return getInstance();
    }
}

3.5 Spring 中的单例

Spring 容器默认管理的 Bean 是单例模式(singleton 作用域),但并非通过上述代码实现,而是通过容器自身的 Bean 工厂机制,确保每次获取的 Bean 是同一个实例。若需多实例,可将作用域改为 prototype。

总结

本文介绍的三种设计模式各有适用场景:策略模式适合处理多种可替换的算法/业务规则,消除分支逻辑;责任链模式适合流程化处理请求,解耦处理者与发送者;单例模式适合确保实例唯一,节约资源。实际开发中需结合业务场景选择合适的实现方式,同时兼顾代码的可扩展性、线程安全性和可维护性,这些也是面试中的高频考点,需熟练掌握核心原理和代码实现。

浏览 1
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报