java后台常用的设计模式
一、创建型模式:让对象的创建更灵活
1. 单例模式
核心:确保一个类只有一个实例,并提供全局访问点。
后台应用:Spring 容器中的 Bean 默认都是单例(@Service、@Repository、@Controller等)。配置类、数据库连接池、线程池、缓存管理器都基于单例。
代码示例(双检锁方式):
java
public class ConfigManager { private static volatile ConfigManager instance; private Properties props = new Properties(); private ConfigManager() { loadConfig(); } public static ConfigManager getInstance() { if (instance == null) { synchronized (ConfigManager.class) { if (instance == null) { instance = new ConfigManager(); } } } return instance; } }✅ Spring 中直接依赖注入单例 Bean,无需自己写双检锁。
2. 工厂模式
核心:将对象的创建逻辑封装起来,让调用方只依赖接口。
常用变体:
简单工厂:一个工厂类根据参数创建不同对象。
// 产品接口 interface Shape { void draw(); } // 具体产品 class Circle implements Shape { @Override public void draw() { System.out.println("画一个圆形"); } } class Rectangle implements Shape { @Override public void draw() { System.out.println("画一个矩形"); } } // 简单工厂(静态工厂方法) class ShapeFactory { public static Shape createShape(String type) { if ("circle".equalsIgnoreCase(type)) { return new Circle(); } else if ("rectangle".equalsIgnoreCase(type)) { return new Rectangle(); } throw new IllegalArgumentException("不支持的形状: " + type); } } // 客户端使用 public class SimpleFactoryDemo { public static void main(String[] args) { Shape circle = ShapeFactory.createShape("circle"); circle.draw(); // 输出: 画一个圆形 Shape rectangle = ShapeFactory.createShape("rectangle"); rectangle.draw(); // 输出: 画一个矩形 } }工厂方法:定义一个用于创建对象的接口,让子类决定实例化哪一个类。
// 产品接口 interface Logger { void log(String message); } // 具体产品 class FileLogger implements Logger { @Override public void log(String message) { System.out.println("[文件日志] " + message); } } class DatabaseLogger implements Logger { @Override public void log(String message) { System.out.println("[数据库日志] " + message); } } // 抽象创建者(工厂) abstract class LoggerFactory { // 工厂方法,由子类实现 public abstract Logger createLogger(); // 业务方法(可选) public void writeLog(String message) { Logger logger = createLogger(); logger.log(message); } } // 具体工厂:文件日志工厂 class FileLoggerFactory extends LoggerFactory { @Override public Logger createLogger() { return new FileLogger(); } } // 具体工厂:数据库日志工厂 class DatabaseLoggerFactory extends LoggerFactory { @Override public Logger createLogger() { return new DatabaseLogger(); } } // 客户端使用 public class FactoryMethodDemo { public static void main(String[] args) { LoggerFactory factory = new FileLoggerFactory(); factory.writeLog("系统启动"); // 输出: [文件日志] 系统启动 factory = new DatabaseLoggerFactory(); factory.writeLog("用户登录"); // 输出: [数据库日志] 用户登录 } }抽象工厂:创建一系列相关或相互依赖的对象。
// 抽象产品A:按钮 interface Button { void paint(); } // 抽象产品B:复选框 interface Checkbox { void check(); } // 具体产品:Windows 风格 class WindowsButton implements Button { @Override public void paint() { System.out.println("渲染 Windows 风格按钮"); } } class WindowsCheckbox implements Checkbox { @Override public void check() { System.out.println("勾选 Windows 风格复选框"); } } // 具体产品:Mac 风格 class MacButton implements Button { @Override public void paint() { System.out.println("渲染 Mac 风格按钮"); } } class MacCheckbox implements Checkbox { @Override public void check() { System.out.println("勾选 Mac 风格复选框"); } } // 抽象工厂:声明创建一系列相关产品的方法 interface GUIFactory { Button createButton(); Checkbox createCheckbox(); } // 具体工厂:Windows 工厂生产整套 Windows 组件 class WindowsFactory implements GUIFactory { @Override public Button createButton() { return new WindowsButton(); } @Override public Checkbox createCheckbox() { return new WindowsCheckbox(); } } // 具体工厂:Mac 工厂生产整套 Mac 组件 class MacFactory implements GUIFactory { @Override public Button createButton() { return new MacButton(); } @Override public Checkbox createCheckbox() { return new MacCheckbox(); } } // 客户端使用 public class AbstractFactoryDemo { public static void main(String[] args) { // 可通过配置决定使用哪套工厂 GUIFactory factory = new WindowsFactory(); // 或 new MacFactory() Button button = factory.createButton(); Checkbox checkbox = factory.createCheckbox(); button.paint(); // 输出: 渲染 Windows 风格按钮 checkbox.check(); // 输出: 勾选 Windows 风格复选框 } }3. 建造者模式
核心:分离复杂对象的设计与实例化,对象参数与参数的依赖逻辑设置完成后,可集中校验参数并一次生成最终不可变对象。
后台应用:
构建复杂的请求对象(如多个可选参数构建 )。
Lombok 的
@Builder注解就是建造者模式。HttpClient的请求构建、StringBuilder的内部思想类似。
示例如下:
public static class Builder { // 参数final可选,需要实现不可变对象时用 // 必填参数,通过构造器传入 private final String orderNo; private final Long userId; private final BigDecimal amount; // 可选参数,提供默认值 private String couponId = null; private String remark = null; private String invoiceTitle = null; private BigDecimal discount = BigDecimal.ZERO; public Builder(String orderNo, Long userId, BigDecimal amount) { this.orderNo = orderNo; this.userId = userId; this.amount = amount; } // 每个可选参数对应一个 with 方法,返回 Builder 本身(链式调用) public Builder couponId(String couponId) { this.couponId = couponId; return this; } public Builder remark(String remark) { this.remark = remark; return this; } public Builder invoiceTitle(String invoiceTitle) { this.invoiceTitle = invoiceTitle; return this; } public Builder discount(BigDecimal discount) { this.discount = discount; return this; } // 构建最终产品,集中校验,一次校验终身有效 public Order build() { // 必填校验 if (orderNo == null || userId == null || amount == null) { throw new IllegalArgumentException("必填参数不能为空"); } // 业务规则校验(例如折扣不能大于金额) if (discount.compareTo(amount) > 0) { throw new IllegalArgumentException("折扣不能大于订单金额"); } return new Order(this); } }4. 原型模式(适当了解)
通过克隆来创建对象,用于创建成本高或动态定制的场景。Java 中的Cloneable接口、Spring 中 Bean 的作用域为prototype时每次获取都会创建新实例(容器内部可能通过克隆或反射实现)。后台中较少直接手写,但BeanUtils.copyProperties思想类似。
二、结构型模式:让类和对象的组合更优雅
1. 代理模式
核心:为另一个对象提供一个替身,以控制对该对象的访问。
1.1 静态代理
// 抽象主题 public interface UserService { void addUser(String name); void deleteUser(String name); } // 真实主题 public class UserServiceImpl implements UserService { @Override public void addUser(String name) { System.out.println("添加用户:" + name); } @Override public void deleteUser(String name) { System.out.println("删除用户:" + name); } } // 静态代理类:实现相同接口,持有目标对象引用 public class UserServiceStaticProxy implements UserService { private UserService target; public UserServiceStaticProxy(UserService target) { this.target = target; } @Override public void addUser(String name) { long start = System.currentTimeMillis(); target.addUser(name); long end = System.currentTimeMillis(); System.out.println("addUser 方法耗时:" + (end - start) + "ms"); } @Override public void deleteUser(String name) { long start = System.currentTimeMillis(); target.deleteUser(name); long end = System.currentTimeMillis(); System.out.println("deleteUser 方法耗时:" + (end - start) + "ms"); } } // 客户端 public class StaticProxyDemo { public static void main(String[] args) { UserService target = new UserServiceImpl(); UserService proxy = new UserServiceStaticProxy(target); proxy.addUser("张三"); proxy.deleteUser("李四"); } }1.2 JDK 动态代理
JDK 动态代理只能代理接口, mybatis的mapper就是基于此。
//定义接口 public interface UserService { void addUser(String username); String getUser(Long id); } //定义真实实现类 public class UserServiceImpl implements UserService { @Override public void addUser(String username) { System.out.println("新增用户:" + username); } @Override public String getUser(Long id) { System.out.println("查询用户:" + id); return "用户" + id; } } //定义 InvocationHandler,所有调用代理对象的方法,最终都会进入 invoke() import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class LogInvocationHandler implements InvocationHandler { private final Object target; public LogInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("方法调用前:" + method.getName()); long start = System.currentTimeMillis(); Object result = method.invoke(target, args); long end = System.currentTimeMillis(); System.out.println("方法调用后:" + method.getName()); System.out.println("耗时:" + (end - start) + "ms"); return result; } } //创建代理对象 import java.lang.reflect.Proxy; public class Main { public static void main(String[] args) { UserService target = new UserServiceImpl(); UserService proxy = (UserService) Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), new LogInvocationHandler(target) ); proxy.addUser("张三"); String user = proxy.getUser(1001L); System.out.println("返回结果:" + user); } }1.3 CGLIB 代理
运行时生成目标类的子类,通过重写父类方法,在方法执行前后加入增强逻辑。代理普通类,无需定义接口。
//目标类,不需要实现接口 public class UserService { public void addUser(String username) { System.out.println("新增用户:" + username); } public String getUser(Long id) { System.out.println("查询用户:" + id); return "用户" + id; } } //定义 CGLIB 拦截器 import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class LogMethodInterceptor implements MethodInterceptor { @Override public Object intercept( Object obj, Method method, Object[] args, MethodProxy methodProxy ) throws Throwable { System.out.println("方法调用前:" + method.getName()); long start = System.currentTimeMillis(); Object result = methodProxy.invokeSuper(obj, args); long end = System.currentTimeMillis(); System.out.println("方法调用后:" + method.getName()); System.out.println("耗时:" + (end - start) + "ms"); return result; } } //创建代理对象 import net.sf.cglib.proxy.Enhancer; public class Main { public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(UserService.class); enhancer.setCallback(new LogMethodInterceptor()); UserService proxy = (UserService) enhancer.create(); proxy.addUser("张三"); String user = proxy.getUser(1001L); System.out.println("返回结果:" + user); } }spring aop 里,目标类实现了接口 -> 默认使用 JDK 动态代理,目标类没有实现接口 -> 使用 CGLIB 代理。
2. 适配器模式
把一个已有类的接口,转换成系统期望的接口
java示例
//系统内部统一定义一个短信发送接口 public interface SmsSender { void send(String phone, String content); } //阿里云短信 SDK,接口长这样 public class AliyunSmsClient { public void sendSms( String phone, String signName, String templateCode, Map<String, String> params ) { ... } } //写一个适配器 public class AliyunSmsAdapter implements SmsSender { private final AliyunSmsClient aliyunSmsClient; public AliyunSmsAdapter(AliyunSmsClient aliyunSmsClient) { this.aliyunSmsClient = aliyunSmsClient; } @Override public void send(String phone, String content) { Map<String, String> params = new HashMap<>(); params.put("content", content); aliyunSmsClient.sendSms( phone, "我的系统", "SMS_10001", params ); } } //再接入一个腾讯云短信 public class TencentSmsClient { public boolean sendMessage(String nationCode, String phoneNumber, String message) { ... } } //再写一个腾讯云适配器 public class TencentSmsAdapter implements SmsSender { private final TencentSmsClient tencentSmsClient; public TencentSmsAdapter(TencentSmsClient tencentSmsClient) { this.tencentSmsClient = tencentSmsClient; } @Override public void send(String phone, String content) { boolean success = tencentSmsClient.sendMessage("+86", phone, content); if (!success) { throw new RuntimeException("腾讯云短信发送失败"); } } }3. 装饰器模式
核心:在不修改原类的情况下,给对象动态叠加新功能
后台应用:Java I/O 流中的BufferedInputStream装饰FileInputStream。
java示例,有一个短信发送功能,后来你想加,
- 发送前后打印日志
- 失败后重试
- 内容敏感词过滤
- 耗时统计
- 限流控制
//短信发送功能 public interface MessageSender { void send(String receiver, String content); } public class SmsMessageSender implements MessageSender { @Override public void send(String receiver, String content) { System.out.println("发送短信给:" + receiver); System.out.println("短信内容:" + content); } } //定义抽象装饰器 public abstract class MessageSenderDecorator implements MessageSender { protected final MessageSender messageSender; public MessageSenderDecorator(MessageSender messageSender) { this.messageSender = messageSender; } @Override public void send(String receiver, String content) { messageSender.send(receiver, content); } } //日志装饰器 public class LogMessageSenderDecorator extends MessageSenderDecorator { public LogMessageSenderDecorator(MessageSender messageSender) { super(messageSender); } @Override public void send(String receiver, String content) { System.out.println("发送消息开始,receiver = " + receiver); messageSender.send(receiver, content); System.out.println("发送消息结束"); } } //重试装饰器 public class RetryMessageSenderDecorator extends MessageSenderDecorator { public RetryMessageSenderDecorator(MessageSender messageSender) { super(messageSender); } @Override public void send(String receiver, String content) { int maxRetry = 3; for (int i = 1; i <= maxRetry; i++) { try { System.out.println("第 " + i + " 次发送"); messageSender.send(receiver, content); return; } catch (Exception e) { if (i == maxRetry) { throw e; } } } } } //敏感词过滤装饰器 public class FilterMessageSenderDecorator extends MessageSenderDecorator { public FilterMessageSenderDecorator(MessageSender messageSender) { super(messageSender); } @Override public void send(String receiver, String content) { String filteredContent = content.replace("敏感词", "***"); messageSender.send(receiver, filteredContent); } } //使用 public class Main { public static void main(String[] args) { MessageSender sender = new SmsMessageSender(); sender = new FilterMessageSenderDecorator(sender); sender = new LogMessageSenderDecorator(sender); sender = new RetryMessageSenderDecorator(sender); sender.send("13800138000", "你的验证码是 123456,包含敏感词"); } }4. 外观模式
核心:为子系统中的一组接口提供一个统一的高层接口,简化调用。
比如一个下单流程,可能涉及:
校验用户 查询商品 校验库存 计算优惠 创建订单 扣减库存 发起支付 发送通知如果 Controller 里直接写:
userService.checkUser(userId); productService.checkProduct(productId); inventoryService.checkStock(productId, count); couponService.calculate(userId, productId); orderService.createOrder(userId, productId, count); inventoryService.deductStock(productId, count); payService.pay(orderNo); messageService.sendOrderMessage(userId, orderNo);问题是:
- Controller 变得很重
- 调用方需要知道太多业务细节
- 顺序不能错
- 后续流程调整时,很多地方都要改
- 复用困难
外观模式就是把这些复杂调用封装起来,对外暴露一个简单方法:
orderFacade.placeOrder(request); placeOrder(...){ userService.checkUser(userId); productService.checkProduct(productId); inventoryService.checkStock(productId, count); couponService.calculate(userId, productId); orderService.createOrder(userId, productId, count); inventoryService.deductStock(productId, count); payService.pay(orderNo); messageService.sendOrderMessage(userId, orderNo); }三、行为型模式:让对象之间的交互更清晰
1. 策略模式
核心:把一组可替换的算法或业务规则封装成不同类,让调用方根据类型选择其中一种执行。
后台应用:
不同业务规则切换:比如金额计算(会员折扣、满减、优惠券),可以定义
PriceStrategy接口,具体策略实现。和工厂模式结合使用,消除
if-else。if ("FULL_REDUCTION".equals(type)) { // 满减 } else if ("PERCENTAGE".equals(type)) { // 折扣 } else if ("NO_DISCOUNT".equals(type)) { // 无优惠 }//定义策略接口 public interface DiscountStrategy { BigDecimal calculate(BigDecimal originalAmount); } //满减策略 public class FullReductionDiscountStrategy implements DiscountStrategy { @Override public BigDecimal calculate(BigDecimal originalAmount) { if (originalAmount.compareTo(new BigDecimal("100")) >= 0) { return originalAmount.subtract(new BigDecimal("20")); } return originalAmount; } } //折扣策略 public class PercentageDiscountStrategy implements DiscountStrategy { @Override public BigDecimal calculate(BigDecimal originalAmount) { return originalAmount.multiply(new BigDecimal("0.8")); } } //无优惠策略 public class NoDiscountStrategy implements DiscountStrategy { @Override public BigDecimal calculate(BigDecimal originalAmount) { return originalAmount; } } //定义优惠类型 public enum DiscountType { FULL_REDUCTION, PERCENTAGE, NO_DISCOUNT } //策略上下文,也可以叫工厂 public class DiscountContext { private final Map<DiscountType, DiscountStrategy> strategyMap = new HashMap<>(); public DiscountContext() { strategyMap.put(DiscountType.FULL_REDUCTION, new FullReductionDiscountStrategy()); strategyMap.put(DiscountType.PERCENTAGE, new PercentageDiscountStrategy()); strategyMap.put(DiscountType.NO_DISCOUNT, new NoDiscountStrategy()); } public BigDecimal calculate(DiscountType discountType, BigDecimal originalAmount) { DiscountStrategy strategy = strategyMap.get(discountType); if (strategy == null) { throw new IllegalArgumentException("不支持的优惠类型:" + discountType); } return strategy.calculate(originalAmount); } } //调用 public class Main { public static void main(String[] args) { DiscountContext discountContext = new DiscountContext(); BigDecimal originalAmount = new BigDecimal("200"); BigDecimal payAmount = discountContext.calculate( DiscountType.FULL_REDUCTION, originalAmount ); System.out.println("原价:" + originalAmount); System.out.println("实付:" + payAmount); } }
2. 观察者模式
核心:一个对象状态发生变化后,自动通知一批依赖它的对象。
后台应用:
Spring 事件机制:
ApplicationEvent+@EventListener或ApplicationListener。例如用户注册后发送邮件、初始化数据等解耦操作。消息队列(MQ)也是广义的观察者,但模式内核一致。
java
//先定义事件对象 public class OrderCreatedEvent { final String orderNo; final Long userId; final Integer amount; } //定义观察者接口 public interface OrderObserver { void onOrderCreated(OrderCreatedEvent event); } //短信观察者 public class SmsObserver implements OrderObserver { @Override public void onOrderCreated(OrderCreatedEvent event) { System.out.println("发送短信,用户ID:" + event.getUserId() + ",订单号:" + event.getOrderNo()); } } //积分观察者 public class PointObserver implements OrderObserver { @Override public void onOrderCreated(OrderCreatedEvent event) { System.out.println("增加积分,用户ID:" + event.getUserId() + ",金额:" + event.getAmount()); } } //优惠券观察者 public class CouponObserver implements OrderObserver { @Override public void onOrderCreated(OrderCreatedEvent event) { System.out.println("发放优惠券,用户ID:" + event.getUserId()); } } //定义主题,也就是事件发布者 public class OrderEventPublisher { private final List<OrderObserver> observers = new ArrayList<>(); public void register(OrderObserver observer) { observers.add(observer); } public void publish(OrderCreatedEvent event) { for (OrderObserver observer : observers) { observer.onOrderCreated(event); } } } //订单服务 public class OrderService { private final OrderEventPublisher eventPublisher; public OrderService(OrderEventPublisher eventPublisher) { this.eventPublisher = eventPublisher; } public void createOrder(Long userId, Integer amount) { String orderNo = "ORDER_1001"; System.out.println("创建订单成功,订单号:" + orderNo); OrderCreatedEvent event = new OrderCreatedEvent(orderNo, userId, amount); eventPublisher.publish(event); } }3. 模板方法模式
核心:把一套固定流程写在父类里,把流程中可变的步骤交给子类实现。
Java 后台常见场景:
- 文件导入:Excel 导入、CSV 导入、JSON 导入
- 支付流程:参数校验、创建支付单、调用渠道、更新状态
- 审批流程:提交、校验、审批、通知
- 定时任务:初始化、执行任务、记录结果、清理资源
- 数据同步:查询数据、转换数据、写入目标系统
- 订单处理:校验订单、计算价格、扣库存、创建订单
Java 示例:文件导入流程
//先定义一个抽象导入模板类 public abstract class AbstractFileImportTemplate<T> { //模板方法:最关键的是这个方法,定义了整个导入流程,而且加了 final,防止子类随便改流程顺序 public final void importFile(String filePath) { checkFile(filePath); List<String> rawDataList = parseFile(filePath); List<T> dataList = convertData(rawDataList); validateData(dataList); saveData(dataList); afterImport(dataList); } protected void checkFile(String filePath) { if (filePath == null || filePath.isEmpty()) { throw new IllegalArgumentException("文件路径不能为空"); } System.out.println("校验文件:" + filePath); } protected abstract List<String> parseFile(String filePath); protected abstract List<T> convertData(List<String> rawDataList); protected void validateData(List<T> dataList) { if (dataList == null || dataList.isEmpty()) { throw new IllegalArgumentException("导入数据不能为空"); } System.out.println("校验数据,数量:" + dataList.size()); } protected abstract void saveData(List<T> dataList); //这个方法就是钩子方法,它有默认实现,子类可以重写 protected void afterImport(List<T> dataList) { System.out.println("导入完成,默认不做额外处理"); } } //Excel 导入实现 //定义用户对象 public class User { String username; String phone; } //Excel 用户导入类 public class ExcelUserImportTemplate extends AbstractFileImportTemplate<User> { @Override protected List<String> parseFile(String filePath) { System.out.println("按照 Excel 格式解析文件:" + filePath); List<String> rawDataList = new ArrayList<>(); rawDataList.add("张三,13800138000"); rawDataList.add("李四,13900139000"); return rawDataList; } @Override protected List<User> convertData(List<String> rawDataList) { System.out.println("把 Excel 原始数据转换成 User 对象"); List<User> users = new ArrayList<>(); for (String rawData : rawDataList) { String[] arr = rawData.split(","); users.add(new User(arr[0], arr[1])); } return users; } @Override protected void saveData(List<User> dataList) { System.out.println("保存用户数据到数据库:" + dataList); } @Override protected void afterImport(List<User> dataList) { System.out.println("发送用户导入完成通知,导入数量:" + dataList.size()); } } //CSV 导入实现 import java.util.ArrayList; import java.util.List; public class CsvUserImportTemplate extends AbstractFileImportTemplate<User> { @Override protected List<String> parseFile(String filePath) { System.out.println("按照 CSV 格式解析文件:" + filePath); List<String> rawDataList = new ArrayList<>(); rawDataList.add("王五,13700137000"); rawDataList.add("赵六,13600136000"); return rawDataList; } @Override protected List<User> convertData(List<String> rawDataList) { System.out.println("把 CSV 原始数据转换成 User 对象"); List<User> users = new ArrayList<>(); for (String rawData : rawDataList) { String[] arr = rawData.split(","); users.add(new User(arr[0], arr[1])); } return users; } @Override protected void saveData(List<User> dataList) { System.out.println("批量保存 CSV 用户数据:" + dataList); } }优点:
- 固定主流程,避免子类乱改执行顺序
- 公共逻辑可以放在父类复用
- 子类只关注差异化步骤
- 适合流程型业务
缺点:
- 依赖继承,父类和子类耦合较强
- 如果流程变化很多,父类容易变复杂
- 子类数量可能变多
4. 责任链模式
核心:把多个处理器按顺序串成一条链,请求沿着链依次处理,每个处理器可以选择处理、放行或中断。
常见场景:
- 登录认证:Token 校验、用户状态校验、权限校验
- 下单校验:参数、用户、商品、库存、风控
- 审批流:组长审批、经理审批、总监审批
- 网关过滤:黑名单、限流、鉴权、日志
- Spring MVC:
Filter、Interceptor - Netty:
ChannelPipeline
Java 示例:下单校验责任链
//请求上下文 public class OrderCreateContext { private Long userId; private Long productId; private Integer count; } //抽象处理器 public abstract class OrderCheckHandler { private OrderCheckHandler next; public OrderCheckHandler setNext(OrderCheckHandler next) { this.next = next; return next; } public final void check(OrderCreateContext context) { doCheck(context); if (next != null) { next.check(context); } } protected abstract void doCheck(OrderCreateContext context); } //参数校验 public class ParamCheckHandler extends OrderCheckHandler { @Override protected void doCheck(OrderCreateContext context) { if (context.getUserId() == null) { throw new IllegalArgumentException("用户ID不能为空"); } if (context.getProductId() == null) { throw new IllegalArgumentException("商品ID不能为空"); } if (context.getCount() == null || context.getCount() <= 0) { throw new IllegalArgumentException("购买数量必须大于0"); } System.out.println("参数校验通过"); } } //用户校验 public class UserCheckHandler extends OrderCheckHandler { @Override protected void doCheck(OrderCreateContext context) { System.out.println("校验用户状态,userId = " + context.getUserId()); boolean userNormal = true; if (!userNormal) { throw new RuntimeException("用户状态异常"); } System.out.println("用户校验通过"); } } //库存校验 public class StockCheckHandler extends OrderCheckHandler { @Override protected void doCheck(OrderCreateContext context) { System.out.println("校验库存,productId = " + context.getProductId()); int stock = 100; if (stock < context.getCount()) { throw new RuntimeException("库存不足"); } System.out.println("库存校验通过"); } } //风控校验 public class RiskCheckHandler extends OrderCheckHandler { @Override protected void doCheck(OrderCreateContext context) { System.out.println("执行风控校验"); boolean riskPass = true; if (!riskPass) { throw new RuntimeException("风控校验不通过"); } System.out.println("风控校验通过"); } } //组装责任链 public class Main { public static void main(String[] args) { OrderCheckHandler paramCheckHandler = new ParamCheckHandler(); paramCheckHandler .setNext(new UserCheckHandler()) .setNext(new StockCheckHandler()) .setNext(new RiskCheckHandler()); OrderCreateContext context = new OrderCreateContext(1001L, 2001L, 2); paramCheckHandler.check(context); System.out.println("所有校验通过,可以创建订单"); } }优点:
- 每个处理器职责单一
- 可以灵活增删处理步骤
- 避免一个方法里堆大量校验逻辑
- 符合开闭原则
- 很适合做校验、过滤、审批、风控
缺点:
- 链路太长时,排查问题要关注执行顺序
- 处理器顺序需要管理好
- 请求可能在中间被中断,调用方要清楚失败原因
一句话总结:责任链模式就是把一串处理逻辑拆成多个处理器,让请求按顺序过关;谁校验失败,谁就中断链路。
四、总结与选型建议
| 分类 | 模式 | 后台最常见落地 |
|---|---|---|
| 创建型 | 单例 | Spring Bean 容器、连接池 |
| 创建型 | 工厂 | BeanFactory、策略对象生成 |
| 创建型 | 建造者 | 复杂 DTO 构建、@Builder |
| 结构型 | 代理 | AOP、事务、Mapper 代理 |
| 结构型 | 适配器 | Spring MVCHandlerAdapter、对接异构系统 |
| 结构型 | 装饰器 | I/O 流、缓存增强 |
| 结构型 | 外观 | Service 层封装多个子系统 |
| 行为型 | 策略 | 多重业务规则、支付/折扣 |
| 行为型 | 观察者 | Spring Event、MQ 广播 |
| 行为型 | 模板方法 | JdbcTemplate、基础抽象 Service |
| 行为型 | 责任链 | Filter、Interceptor、审批流 |
使用原则:
不要为了模式而模式,先考虑是否真正解耦了变化点。
善用框架的内置模式(Spring AOP、事件、责任链的 Filter),不要重复造轮子。
消除大量
if-else时优先考虑策略 + 工厂;串联处理流程时考虑责任链;功能增强用代理/装饰器。设计模式是手段,清晰、可测试、可维护的代码才是最终目的。
把这些模式融入日常开发,你会发现代码结构更优雅,需求变更也不再可怕。
