当一个复杂的业务请求需要经过多个处理节点时,例如参数校验、权限检查、业务规则验证等,我们通常会怎么写代码?一个常见的做法是在业务方法中使用大量的 if-else 语句嵌套,或者在代码中硬编码处理步骤的调用顺序。

这种方式有什么问题呢?代码耦合度高、难以维护、不利于扩展和单元测试。当需要新增或调整处理步骤时,往往需要深入修改核心业务逻辑,违反了开闭原则。幸运的是,结合两种经典的设计模式——责任链模式和策略模式,可以优雅地解决这一问题。
一、什么是责任链模式?
责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。它将请求的发送者和接收者解耦,允许多个对象都有机会处理这个请求。每个处理器都包含对下一个处理器的引用。当请求到来时,会沿着链条依次传递,直到有一个处理器处理它为止,或者链条结束。

这种模式非常适合处理多步骤、流水线式的业务场景,如审批流、过滤器链等。
二、什么是策略模式?
策略模式(Strategy Pattern)定义了一系列算法,并将每一个算法封装起来,使它们可以相互替换。策略模式让算法的变化独立于使用算法的客户。

这种模式常用于同一类问题有多种不同解决策略的情况,例如支付方式选择(支付宝、微信、银行卡)、数据验证规则集合等。
三、两种模式如何结合?
我们可以将责任链模式作为宏观框架,用于组织请求的处理流程;而在责任链的某个具体节点(处理器)内部,使用策略模式来管理该节点下多种可选的、可替换的具体行为。这样既能保证处理流程的清晰和可配置性,又能保证每个处理步骤内部逻辑的灵活性与可扩展性。

四、实现思路详解
4.1 定义统一的处理接口
首先我们需要定义一个统一的处理器接口,这是责任链的基础:
// 处理器接口
public interface RequestHandler {
/**
* 处理请求
* @param context 请求上下文
* @return 是否继续处理
*/
boolean handle(RequestContext context);
/**
* 获取处理器名称
*/
String getName();
}
4.2 定义请求上下文
为了在处理链中传递数据和状态,我们需要一个上下文对象:
// 请求上下文
public class RequestContext {
private Map<String, Object> attributes = new HashMap<>();
private Object requestData;
private Object responseData;
private List<String> processLog = new ArrayList<>();
// getter/setter方法...
public void addProcessLog(String log) {
processLog.add(log);
}
}
4.3 实现责任链接口
接下来实现责任链的核心抽象逻辑,它定义了处理器的链接关系和执行顺序:
// 责任链接口
public abstract class AbstractHandlerChain implements RequestHandler {
protected RequestHandler nextHandler;
public void setNextHandler(RequestHandler handler) {
this.nextHandler = handler;
}
@Override
public boolean handle(RequestContext context) {
// 当前处理器处理
boolean result = doHandle(context);
// 如果处理成功且有下一个处理器,则继续处理
if (result && nextHandler != null) {
return nextHandler.handle(context);
}
return result;
}
// 子类需要实现的具体处理逻辑
protected abstract boolean doHandle(RequestContext context);
}
4.4 实现具体的处理器
现在我们可以实现各种具体的处理器了,这些处理器通常会使用 Spring 的 @Component 注解进行管理:
// 参数校验处理器
@Component
public class ParameterValidationHandler extends AbstractHandlerChain {
@Override
public String getName() {
return "参数校验处理器";
}
@Override
protected boolean doHandle(RequestContext context) {
// 获取请求数据
Object requestData = context.getRequestData();
// 执行参数校验逻辑
if (requestData == null) {
context.addProcessLog("参数为空,校验失败");
return false;
}
// 更多校验逻辑...
context.addProcessLog("参数校验通过");
return true;
}
}
// 权限检查处理器
@Component
public class PermissionCheckHandler extends AbstractHandlerChain {
@Override
public String getName() {
return "权限检查处理器";
}
@Override
protected boolean doHandle(RequestContext context) {
// 获取用户信息
String userId = (String) context.getAttribute("userId");
// 执行权限检查逻辑
if (!hasPermission(userId)) {
context.addProcessLog("用户权限不足");
return false;
}
context.addProcessLog("权限检查通过");
return true;
}
private boolean hasPermission(String userId) {
// 实际的权限检查逻辑
return true; // 简化示例
}
}
4.5 策略模式的应用
对于同一类型的处理器,我们可以用策略模式来实现不同的处理策略。以“业务规则校验”这个处理器为例,其内部可以包含多种校验策略:
// 业务规则校验策略接口
public interface BusinessRuleStrategy {
boolean validate(RequestContext context);
String getRuleName();
}
// 订单金额校验策略
@Component
public class OrderAmountRuleStrategy implements BusinessRuleStrategy {
@Override
public boolean validate(RequestContext context) {
// 获取订单信息
OrderInfo orderInfo = (OrderInfo) context.getRequestData();
// 校验金额是否合理
if (orderInfo.getAmount().compareTo(BigDecimal.valueOf(100000)) > 0) {
context.addProcessLog("订单金额过大");
return false;
}
return true;
}
@Override
public String getRuleName() {
return "订单金额校验";
}
}
// 库存校验策略
@Component
public class InventoryRuleStrategy implements BusinessRuleStrategy {
@Override
public boolean validate(RequestContext context) {
// 获取商品信息
ProductInfo productInfo = (ProductInfo) context.getRequestData();
// 校验库存是否充足
if (productInfo.getStock() < productInfo.getQuantity()) {
context.addProcessLog("库存不足");
return false;
}
return true;
}
@Override
public String getRuleName() {
return "库存校验";
}
}
// 业务规则校验处理器(使用策略模式)
@Component
public class BusinessRuleHandler extends AbstractHandlerChain {
@Autowired
private List<BusinessRuleStrategy> strategies;
@Override
public String getName() {
return "业务规则校验处理器";
}
@Override
protected boolean doHandle(RequestContext context) {
// 依次执行所有业务规则策略
for (BusinessRuleStrategy strategy : strategies) {
if (!strategy.validate(context)) {
context.addProcessLog("业务规则校验失败: " + strategy.getRuleName());
return false;
}
context.addProcessLog("业务规则校验通过: " + strategy.getRuleName());
}
return true;
}
}
4.6 链的组装和管理
为了让责任链更加灵活和可配置,我们需要一个链的管理器来负责处理器的动态组装。这体现了Spring Boot等框架依赖注入和容器管理的优势。
// 处理链管理器
@Component
public class HandlerChainManager {
@Autowired
private List<RequestHandler> handlers;
// 根据配置组装处理链
public RequestHandler buildChain(List<String> handlerNames) {
if (handlerNames == null || handlerNames.isEmpty()) {
return null;
}
RequestHandler head = null;
RequestHandler current = null;
for (String handlerName : handlerNames) {
RequestHandler handler = findHandlerByName(handlerName);
if (handler != null) {
if (head == null) {
head = handler;
current = handler;
} else {
((AbstractHandlerChain) current).setNextHandler(handler);
current = handler;
}
}
}
return head;
}
private RequestHandler findHandlerByName(String name) {
return handlers.stream()
.filter(h -> h.getName().equals(name))
.findFirst()
.orElse(null);
}
}
五、使用示例
现在让我们看看在业务服务中如何使用这套机制:
// 服务类
@Service
public class RequestProcessingService {
@Autowired
private HandlerChainManager chainManager;
public ProcessResult processRequest(Object requestData) {
// 创建请求上下文
RequestContext context = new RequestContext();
context.setRequestData(requestData);
context.setAttribute("userId", getCurrentUserId());
// 定义处理链顺序(可从配置文件或数据库中读取)
List<String> handlerSequence = Arrays.asList(
"参数校验处理器",
"权限检查处理器",
"业务规则校验处理器",
"业务逻辑处理器"
);
// 组装处理链
RequestHandler chain = chainManager.buildChain(handlerSequence);
// 执行处理
boolean success = chain != null && chain.handle(context);
// 返回处理结果
return ProcessResult.builder()
.success(success)
.processLogs(context.getProcessLog())
.responseData(context.getResponseData())
.build();
}
private String getCurrentUserId() {
// 获取当前用户ID的逻辑
return "USER001";
}
}
5.1 性能优化:加入缓存机制
为了避免每次请求都重新反射和组装处理链,我们可以引入缓存机制:
// 带缓存的处理链管理器
@Component
public class CachedHandlerChainManager extends HandlerChainManager {
private final Map<String, RequestHandler> chainCache = new ConcurrentHashMap<>();
public RequestHandler getOrCreateChain(String chainKey, List<String> handlerNames) {
return chainCache.computeIfAbsent(chainKey, k -> buildChain(handlerNames));
}
}
5.2 增强健壮性:异常处理
在实际应用中,我们需要确保某个处理器的异常不会导致整个系统崩溃,并能进行适当的日志记录和后续处理。这需要对基础的设计模式抽象进行增强。
// 带异常处理的责任链接口
public abstract class ExceptionSafeHandlerChain extends AbstractHandlerChain {
@Override
public boolean handle(RequestContext context) {
try {
boolean result = doHandle(context);
if (result && nextHandler != null) {
return nextHandler.handle(context);
}
return result;
} catch (Exception e) {
// 记录异常日志
context.addProcessLog("处理器执行异常: " + getName() + ", 错误: " + e.getMessage());
// 根据业务需求决定是否继续执行下一个处理器
return handleException(e, context);
}
}
protected boolean handleException(Exception e, RequestContext context) {
// 默认情况下,异常会导致处理链中断
return false;
}
}
六、总结
通过将责任链模式与策略模式相结合,我们构建了一个高度解耦、灵活可扩展的请求处理框架。这种架构的优势在于:
- 可配置性:处理流程的顺序可以通过配置动态调整,无需修改代码。
- 可扩展性:新增处理步骤只需实现新的处理器并注册到 Spring 容器;在策略模式下,新增校验规则也只需新增策略实现类。
- 可维护性:每个处理器职责单一,代码逻辑清晰,便于单元测试。
- 可观测性:通过请求上下文,可以完整记录请求的处理轨迹和日志,便于调试和监控。
在实际项目中,我们可以根据具体的业务需求来调整这种模式的实现细节,例如增加异步处理支持、优先级调度、监控埋点等,但其核心思想不变:通过合理的抽象和组合,让复杂的业务逻辑变得清晰可控。