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