Java 实现责任链模式 + 策略模式:优雅处理多级请求的方式
一、什么是责任链模式?
责任链模式(Chain of Responsibility Pattern) 是一种行为设计模式,它允许将请求沿着一个处理链传递,直到链中的某个对象处理它。这样,发送者无需知道哪个对象将处理请求,所有的处理对象都可以尝试处理请求或将请求传递给链上的下一个对象。
核心思想:将请求的发送者与接收者解耦,通过让多个对象组成一条链,使得请求沿着链传递,直到被处理。
二、责任链模式的特点
- 解耦请求发出者和处理者:请求的发送者不需要知道具体的处理者是谁,增强了系统的灵活性和扩展性。
- 动态组合处理逻辑:可以根据需要动态改变链的结构,添加或移除处理者。
- 职责单一:责任链模式可以将每个验证逻辑封装到一个独立的处理器中,每个处理器负责单一的验证职责,符合单一职责原则。
- 可扩展性: 增加新的验证逻辑时,处理者只需继承一个统一的接口,并添加新的处理器,而不需要修改现有的代码。
- 清晰的流程: 将所有验证逻辑组织在一起,使得代码结构更加清晰,易于理解。
三、责任链模式和策略模式结合的意义
- 责任链模式的作用:
- 用于动态处理请求,将多个处理逻辑串联起来。
- 策略模式的作用:
- 用于封装一组算法,使得可以在运行时动态选择需要的算法。
结合两者:
- 责任链模式负责串联和传递请求,而策略模式定义了每一个处理者的具体处理逻辑。
- 两者结合可以实现既动态构建责任链,又灵活应用不同策略来处理请求的需求。
四、责任链模式解决的问题
- 耦合过高:将请求的处理者从请求的发送者中解耦,使得处理者可以独立扩展或变更。
- 复杂的多条件判断:避免在代码中使用过多
if-else
或switch-case
语句。 - 灵活性不足:通过链的动态组合可以轻松调整请求的传递逻辑或插入新的处理者。
- 代码重复:每个处理者只专注于处理它关心的部分,减少重复代码。
五、代码中的责任链模式解析
场景 1:商品上架逻辑(多重校验)
实现一个类似的场景——商品上架逻辑(如校验商品信息、库存信息等),可以按照以下步骤实现:
- 定义责任链抽象接口
public interface MerchantAdminAbstractChainHandlerextends Ordered {
/**
* 执行责任链逻辑
*
* @param requestParam 责任链执行入参
*/
void handler(T requestParam);
/**
* @return 责任链组件标识
*/
String mark();
}
- 定义商品上架的责任链标识:
public enum ChainBizMarkEnum {
MERCHANT_ADMIN_CREATE_PRODUCT_TEMPLATE_KEY,
MERCHANT_ADMIN_PRODUCT_UPSHELF_KEY; // 新增商品上架责任链标识
}
- 定义每个处理器的通用行为:
@Component
public final class MerchantAdminChainContextimplements ApplicationContextAware, CommandLineRunner {
/**
* 应用上下文,通过Spring IOC获取Bean实例
*/
private ApplicationContext applicationContext;
/**
* 保存商品上架责任链实现类
*
* Key:{@link MerchantAdminAbstractChainHandler#mark()}
* Val:{@link MerchantAdminAbstractChainHandler} 一组责任链实现 Spring Bean 集合
*
* 比如有一个商品上架模板创建责任链,实例如下:
* Key:MERCHANT_ADMIN_CREATE_PRODUCT_TEMPLATE_KEY
* Val:
* - 验证商品信息基本参数是否必填 —— 执行器 {@link ProductInfoNotNullChainFilter}
* - 验证商品库存 —— 执行器 {@link ProductInventoryCheckChainFilter}
*/
private final Map> abstractChainHandlerContainer = new HashMap<>();
/**
* 责任链组件执行
* @param mark 责任链组件标识
* @param requestObj 请求参数
*/
public void handler(String mark,T requestObj){
// 根据 mark 标识从责任链容器中获取一组责任链实现 Bean 集合
List abstractChainHandlers = abstractChainHandlerContainer.get(mark);
if (CollectionUtils.isEmpty(abstractChainHandlers)) {
throw new RuntimeException(String.format("[%s] Chain of Responsibility ID is undefined.", mark));
}
abstractChainHandlers.forEach(each -> each.handler(requestObj));
}
/**
* 执行方法,接收可变参数
* 本方法主要用于初始化和处理商品上架抽象责任链容器
* 它从Spring容器中获取所有MerchantAdminAbstractChainHandler类型的Bean,
* 并根据它们的mark进行分类和排序,以便后续处理
*
* @param args 可变参数,可能包含方法运行所需的额外信息
* @throws Exception 如果方法执行过程中遇到错误,抛出异常
*/
@Override
public void run(String... args) throws Exception {
// 从 Spring IOC 容器中获取指定接口 Spring Bean 集合
Map chainFilterMap = applicationContext.getBeansOfType(MerchantAdminAbstractChainHandler.class);
// 遍历所有获取到的Bean,并将它们根据mark分类存入抽象责任链容器中
chainFilterMap.forEach((beanName, bean) -> {
// 判断 Mark 是否已经存在抽象责任链容器中,如果已经存在直接向集合新增;如果不存在,创建 Mark 和对应的集合
List abstractChainHandlers = abstractChainHandlerContainer.getOrDefault(bean.mark(), new ArrayList<>());
abstractChainHandlers.add(bean);
abstractChainHandlerContainer.put(bean.mark(), abstractChainHandlers);
});
// 遍历抽象责任链容器,对每个 Mark 对应的责任链实现类集合进行排序
abstractChainHandlerContainer.forEach((mark, chainHandlers) -> {
// 对每个 Mark 对应的责任链实现类集合进行排序,优先级小的在前
chainHandlers.sort(Comparator.comparing(Ordered::getOrder));
});
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
,>,>
- 定义商品上架的责任链处理器:
@Component
public class ProductInfoNotNullChainFilter implements MerchantAdminAbstractChainHandler {
@Override
public void handler(ProductUpShelfReqDTO requestParam) {
if (StringUtils.isEmpty(requestParam.getProductName())) {
throw new RuntimeException("商品名称不能为空!");
}
if (requestParam.getPrice() == null || requestParam.getPrice() <= 0) {
throw new RuntimeException("商品价格必须大于0!");
}
System.out.println("商品信息非空校验通过");
}
@Override
public int getOrder() {
return 1;
}
@Override
public String mark() {
return ChainBizMarkEnum.MERCHANT_ADMIN_PRODUCT_UPSHELF_KEY.name();
}
}
@Component
public class ProductInventoryCheckChainFilter implements MerchantAdminAbstractChainHandler {
@Override
public void handler(ProductUpShelfReqDTO requestParam) {
if (requestParam.getStock() <= 0) {
throw new RuntimeException("商品库存不足,无法上架!");
}
System.out.println("商品库存校验通过");
}
@Override
public int getOrder() {
return 2;
}
@Override
public String mark() {
return ChainBizMarkEnum.MERCHANT_ADMIN_PRODUCT_UPSHELF_KEY.name();
}
}
- 调用责任链进行处理:
@Service
@RequiredArgsConstructor
public class ProductServiceImpl {
private final MerchantAdminChainContextmerchantAdminChainContext;
public void upShelfProduct(ProductUpShelfReqDTO requestParam) {
// 调用责任链进行校验
merchantAdminChainContext.handler(
ChainBizMarkEnum.MERCHANT_ADMIN_PRODUCT_UPSHELF_KEY.name(),
requestParam
);
System.out.println("商品上架逻辑开始执行...");
// 后续的商品上架业务逻辑
}
}
上述代码实现了一个基于 责任链模式 的电商系统,主要用于处理复杂的业务逻辑,如商品上架模板的创建。这种模式的设计使得每个业务逻辑通过一个独立的处理器(Handler)进行处理,并将这些处理器串联成一个链,通过统一的入口执行每一步处理操作。
1. 代码的组成部分与职责解析
(1) 责任链抽象接口:MerchantAdminAbstractChainHandler
- 定义了责任链中的基础行为:
void handler(T requestParam)
- 定义了该处理器的具体逻辑。
- 这是责任链的核心方法,每个处理器都会接收到传入的参数
requestParam
,并根据具体的业务逻辑进行相应的处理。
- 设计思想:
T
是一个泛型参数,可以适配不同类型的业务场景(如对象校验、数据处理等)。- 如果某个处理器不满足条件,可以抛出异常或者提供返回值来中断后续处理器的运行。
- 每个处理器只负责完成自己的一部分逻辑,保持模块化设计。
(2) 抽象处理器接口:MerchantAdminAbstractChainHandler
- 定义了责任链中每个节点的通用行为:
void handler(T requestParam)
- 责任链的核心方法,定义了如何处理传入的请求参数
requestParam
。 - 每个实现类都会根据具体的业务需求,在该方法中实现自己的处理逻辑,
比如参数校验、数据转换
等。 - 如果某个处理环节中发生错误,可以通过抛出异常中断责任链的执行。
handler(T requestParam)
:执行具体的处理逻辑。mark()
:返回处理器所属的责任链标识(Mark
)。
- 责任链的核心方法,定义了如何处理传入的请求参数
String mark()
- 返回当前处理器所属的责任链标识(Mark)。
- 不同的责任链可以通过
mark()
值进行分组管理。 - 比如在商品上架创建责任链中,
mark()
可以返回MERCHANT_ADMIN_CREATE_PRODUCT_TEMPLATE_KEY
。
int getOrder()
- 用于定义处理器的执行顺序。
- 通过实现
Ordered
接口的getOrder()
方法,开发者可以灵活地控制每个处理器在责任链中的执行顺序。 - 默认值为
Ordered.LOWEST_PRECEDENCE
(优先级最低),可以根据需求覆盖此方法返回更高的优先级(数值越小优先级越高)。
- 通过继承
Ordered
接口来用于指定处理器的执行顺序,优先级小的会先执行。(模版如下)
import org.springframework.core.Ordered;
/**
* 商家上架责任链处理器抽象接口
*
* @param 处理参数的泛型类型(比如请求参数)
*/
public interface MerchantAdminAbstractChainHandlerextends Ordered {
/**
* 执行责任链的具体逻辑
*
* @param requestParam 责任链执行的入参
*/
void handler(T requestParam);
/**
* 获取责任链处理器的标识(mark)
*
* 每个处理器所属的责任链标识需要唯一,用于区分不同的责任链。
*
* @return 责任链组件标识
*/
String mark();
/**
* 获取责任链执行顺序
*
* Spring 的 {@link Ordered} 接口方法,数值越小优先级越高。
* 默认返回 `Ordered.LOWEST_PRECEDENCE`,表示优先级最低。
*
* @return 处理器的执行顺序。
*/
@Override
default int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}
(2) 责任链上下文:MerchantAdminChainContext
- 负责管理责任链的初始化和执行:
- 在 Spring 容器启动时 (
CommandLineRunner
),扫描实现了MerchantAdminAbstractChainHandler
接口的所有 Spring Bean,并根据它们的mark()
属性将它们归类到不同的链条中。 - 在链条内部,根据
Ordered
的优先级对处理器进行排序。 - 提供统一的
handler()
方法,根据标识 (Mark
) 执行对应的责任链。
- 在 Spring 容器启动时 (
(3) 业务服务层:ProductInventoryCheckChainFilter
- 通过
MerchantAdminChainContext
调用对应的责任链,完成业务参数校验逻辑。 - 责任链完成校验后,后续可以继续执行其他具体的业务逻辑。
责任链的执行流程
通过 MerchantAdminChainContext
,上述两个处理器会被自动扫描并加载到责任链中。运行时,根据 mark()
和 getOrder()
的值,系统自动按顺序执行它们。
五、Java 实现责任链模式 + 策略模式
以下是实现一个责任链 + 策略模式的完整 Java 示例。
场景:模拟用户请求的审核流程(如普通用户审批、管理员审批、高级管理员审批),并结合不同策略处理请求。
1. 定义处理请求的接口
// 抽象处理者接口
public interface RequestHandler {
// 设置下一个处理者
void setNextHandler(RequestHandler nextHandler);
// 处理请求的方法
void handleRequest(UserRequest request);
}
2. 定义用户请求类
// 请求类
public class UserRequest {
private String userType; // 用户类型(普通用户、管理员等)
private String requestContent; // 请求内容
public UserRequest(String userType, String requestContent) {
this.userType = userType;
this.requestContent = requestContent;
}
public String getUserType() {
return userType;
}
public String getRequestContent() {
return requestContent;
}
}
3. 定义不同的策略(处理逻辑)
// 策略接口
public interface RequestStrategy {
void process(UserRequest request);
}
// 普通用户处理策略
public class BasicUserStrategy implements RequestStrategy {
@Override
public void process(UserRequest request) {
System.out.println("普通用户的请求正在处理:" + request.getRequestContent());
}
}
// 管理员处理策略
public class AdminUserStrategy implements RequestStrategy {
@Override
public void process(UserRequest request) {
System.out.println("管理员的请求正在处理:" + request.getRequestContent());
}
}
// 高级管理员处理策略
public class SuperAdminStrategy implements RequestStrategy {
@Override
public void process(UserRequest request) {
System.out.println("高级管理员的请求正在处理:" + request.getRequestContent());
}
}
4. 实现责任链模式的处理者
// 具体处理者,结合策略
public class RequestHandlerImpl implements RequestHandler {
private RequestStrategy strategy; // 策略
private RequestHandler nextHandler; // 下一个处理者
public RequestHandlerImpl(RequestStrategy strategy) {
this.strategy = strategy;
}
@Override
public void setNextHandler(RequestHandler nextHandler) {
this.nextHandler = nextHandler;
}
@Override
public void handleRequest(UserRequest request) {
// 策略处理
strategy.process(request);
// 将请求传递给下一个处理者
if (nextHandler != null) {
nextHandler.handleRequest(request);
}
}
}
5. 测试责任链 + 策略模式
public class ChainStrategyExample {
public static void main(String[] args) {
// 创建策略
RequestStrategy basicStrategy = new BasicUserStrategy();
RequestStrategy adminStrategy = new AdminUserStrategy();
RequestStrategy superAdminStrategy = new SuperAdminStrategy();
// 创建责任链处理者,并设置链条
RequestHandler basicHandler = new RequestHandlerImpl(basicStrategy);
RequestHandler adminHandler = new RequestHandlerImpl(adminStrategy);
RequestHandler superAdminHandler = new RequestHandlerImpl(superAdminStrategy);
basicHandler.setNextHandler(adminHandler);
adminHandler.setNextHandler(superAdminHandler);
// 模拟用户请求
UserRequest basicRequest = new UserRequest("普通用户", "请求访问资源 A");
UserRequest adminRequest = new UserRequest("管理员", "请求修改资源 B");
UserRequest superAdminRequest = new UserRequest("高级管理员", "请求删除资源 C");
// 处理请求
System.out.println("处理普通用户请求:");
basicHandler.handleRequest(basicRequest);
System.out.println("\n处理管理员请求:");
adminHandler.handleRequest(adminRequest);
System.out.println("\n处理高级管理员请求:");
superAdminHandler.handleRequest(superAdminRequest);
}
}
六、为何责任链模式和策略模式结合使用?
- 责任链控制流程,策略定义处理逻辑:
- 责任链模式将处理请求的逻辑连接成链,便于动态调整请求传递的流程。
- 策略模式将处理逻辑封装为独立的策略,可以灵活复用和替换。
- 职责分离:
- 责任链模式负责管理请求的传递,策略模式专注于实现具体的业务逻辑。
- 结合使用可以让代码结构更清晰,职责分配更明确。
- 增强灵活性和可扩展性:
- 责任链可以动态增删处理者,策略可以动态选择或扩展新的处理逻辑,两者结合大大增强了系统的适配性和扩展性。
通过责任链模式与策略模式的结合,可以应对复杂的处理流程和多变的业务需求,同时保持代码的简洁与高内聚的设计结构。
来源:juejin.cn/post/7457366224823124003