当前位置: 首页 > news >正文

设计模式-责任链模式

责任链模式

责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。

责任链模式通过将多个处理器(处理对象)以链式结构连接起来,使得请求沿着这条链传递,直到有一个处理器处理该请求为止。

责任链模式允许多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求。

介绍

意图

允许将请求沿着处理者链传递,直到请求被处理为止。

主要解决的问题

  • 职责单一:解耦请求发送者和接收者,使多个对象都有可能接收请求,而发送者不需要知道哪个对象会处理它。
  • 可拓展性:增加新的验证逻辑时,只需要添加新的处理器,而不需要修改现有的代码。
  • 清晰的流程:将所有验证逻辑组织在一起,使得代码结构更加清晰,易于理解。

使用场景

  • 当有多少对象可以处理请求,且具体由哪个对象处理由运行时决定时。
  • 当需要向多个对象中的一个提交请求,而不想明确指定接收者时。

实现方式

  • 定义处理者接口:所有处理者必须实现同一个接口
  • 创建具体处理者:实现接口的具体类,包含请求处理逻辑和指向链中下一个处理者的引用。

关键代码

  • Handler接口:定义一个方法用于处理请求。
  • ConcreteHandler类:实现Handler接口,包含请求逻辑和对下一个处理者的引用。

优点

  • 降低耦合度:发送者和接收者之间解耦。
  • 简化对象:对象不需要知道链的结构。
  • 灵活性:通过改变链的成员或顺序,动态地新增或删除责任。
  • 易于扩展:增加新的请求处理类很方便。

缺点

  • 请求未被处理:不能保证请求一定会被链中的某个处理者接收。
  • 性能影响:可能影响系统性能,且调试困难,可能导致循环调用。
  • 难以观察:运行时特征不明显,可能妨碍除错。

使用建议

  • 在处理请求时,如果有多个潜在的处理者,考虑使用责任链模式。
  • 确保链中的每个处理者都明确知道如何传递请求到链的下一个环节。

实际应用

📢注意:我这里在项目中应用时,新建一个责任链上下文容器MerchantAdminChainContext来存储保存商家后管责任链实现类。跟上面也差不多。

image

实现逻辑

定义责任链抽象接口

/*** 抽象商家后管业务责任链组件*/
public interface MerchantAdminAbstractChainHandler<T> extends Ordered {/*** 执行责任链逻辑** @param requestParam 责任链执行入参*/void handler(T requestParam);/*** @return 责任链组件标识*/String mark();
}

创建责任链模式上下文容器

/*** 商家后管责任链上下文容器* ApplicationContextAware 接口获取应用上下文,并复制局部变量方便后续使用;CommandLineRunner 项目启动后执行责任链容器的填充工作*/
@Component
public final class MerchantAdminChainContext<T> implements ApplicationContextAware, CommandLineRunner {/*** 应用上下文,我们这里通过 Spring IOC 获取 Bean 实例*/private ApplicationContext applicationContext;/*** 保存商家后管责任链实现类* <p>* Key:{@link MerchantAdminAbstractChainHandler#mark()}* Val:{@link MerchantAdminAbstractChainHandler} 一组责任链实现 Spring Bean 集合* <p>* 比如有一个优惠券模板创建责任链,实例如下:* Key:MERCHANT_ADMIN_CREATE_COUPON_TEMPLATE_KEY* Val:* - 验证优惠券信息基本参数是否必填 —— 执行器 {@link CouponTemplateCreateParamNotNullChainFilter}* - 验证优惠券信息基本参数是否按照格式传递 —— 执行器 {@link CouponTemplateCreateParamBaseVerifyChainFilter}* - 验证优惠券信息基本参数是否正确,比如商品数据是否存在等 —— 执行器 {@link CouponTemplateCreateParamVerifyChainFilter}* - ......*/private final Map<String, List<MerchantAdminAbstractChainHandler>> abstractChainHandlerContainer = new HashMap<>();/*** 责任链组件执行** @param mark         责任链组件标识* @param requestParam 请求参数*/public void handler(String mark, T requestParam) {// 根据 mark 标识从责任链容器中获取一组责任链实现 Bean 集合List<MerchantAdminAbstractChainHandler> 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(requestParam));}@Overridepublic void run(String... args) throws Exception {// 从 Spring IOC 容器中获取指定接口 Spring Bean 集合Map<String, MerchantAdminAbstractChainHandler> chainFilterMap = applicationContext.getBeansOfType(MerchantAdminAbstractChainHandler.class);chainFilterMap.forEach((beanName, bean) -> {// 判断 Mark 是否已经存在抽象责任链容器中,如果已经存在直接向集合新增;如果不存在,创建 Mark 和对应的集合List<MerchantAdminAbstractChainHandler> abstractChainHandlers = abstractChainHandlerContainer.getOrDefault(bean.mark(), new ArrayList<>());abstractChainHandlers.add(bean);abstractChainHandlerContainer.put(bean.mark(), abstractChainHandlers);});abstractChainHandlerContainer.forEach((mark, unsortedChainHandlers) -> {// 对每个 Mark 对应的责任链实现类集合进行排序,优先级小的在前unsortedChainHandlers.sort(Comparator.comparing(Ordered::getOrder));});}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}
}

创建具体业务责任链实现类

非空验证

@Component
public class CouponTemplateCreateParamNotNullChainFilter implements MerchantAdminAbstractChainHandler<CouponTemplateSaveReqDTO> {@Overridepublic void handler(CouponTemplateSaveReqDTO requestParam) {if (StrUtil.isEmpty(requestParam.getName())) {throw new ClientException("优惠券名称不能为空");}if (ObjectUtil.isEmpty(requestParam.getSource())) {throw new ClientException("优惠券来源不能为空");}if (ObjectUtil.isEmpty(requestParam.getTarget())) {throw new ClientException("优惠对象不能为空");}if (ObjectUtil.isEmpty(requestParam.getType())) {throw new ClientException("优惠类型不能为空");}if (ObjectUtil.isEmpty(requestParam.getValidStartTime())) {throw new ClientException("有效期开始时间不能为空");}if (ObjectUtil.isEmpty(requestParam.getValidEndTime())) {throw new ClientException("有效期结束时间不能为空");}if (ObjectUtil.isEmpty(requestParam.getStock())) {throw new ClientException("库存不能为空");}if (StrUtil.isEmpty(requestParam.getReceiveRule())) {throw new ClientException("领取规则不能为空");}if (StrUtil.isEmpty(requestParam.getConsumeRule())) {throw new ClientException("消耗规则不能为空");}}@Overridepublic String mark() {return MERCHANT_ADMIN_CREATE_COUPON_TEMPLATE_KEY.name();}@Overridepublic int getOrder() {return 0;}
}

如何使用?

@Service
@RequiredArgsConstructor
public class CouponTemplateServiceImpl extends ServiceImpl<CouponTemplateMapper, CouponTemplateDO> implements CouponTemplateService {private final MerchantAdminChainContext merchantAdminChainContext;@Overridepublic void createCouponTemplate(CouponTemplateSaveReqDTO requestParam) {// 通过责任链验证请求参数是否正确merchantAdminChainContext.handler(ChainBizMarkEnum.MERCHANT_ADMIN_CREATE_COUPON_TEMPLATE_KEY.name(), requestParam);// xxxxxx}
}

至此,基于责任链模式验证请求参数成功实现。若后续要进行新的验证,可创建具体业务责任链实现类。

http://www.zskr.cn/news/2287.html

相关文章:

  • 实用指南:Grafana - 监控磁盘使用率Variables使用
  • P4694 [PA 2013] Raper
  • C# 内存泄漏
  • TVBox中的Python接口解读
  • DevOps时代的知识管理革命:如何构建智能化的研发决策中枢
  • P1099 [NOIP 2007 提高组] 树网的核
  • C# Avalonia 13- MoreDrawing - VisualLayer
  • Linux 设置nginx 以及java jar自启动
  • 记录一次解决phpstudy启动数据库自动关闭的问题方法
  • node.js安装地址
  • 【已解决】git Encountered 3 file(s) that should have been pointers, but werent
  • 接雨水-leetcode
  • QT-控件使用-获取lable标签宽高尺寸设置图片
  • 初识python:一些基础的知识(推导式)
  • 小说写法分析-个人随记
  • Nuget的不是所配置的源之一
  • k60刷windows系统能玩什么游戏
  • 微服务高可用高并发方案
  • pip安装临时使用清华源
  • redis scan命令替换keys 命令
  • 记一次 .NET 某企业ECM内容管理系统 内存暴涨分析
  • 可编辑区域
  • docker-compose安装PostgreSQL和pgvector向量数据库
  • 【连续五届稳定检索、院士杰青云集】第六届先进材料与智能制造国际学术会议(ICAMIM 2025)
  • macbook airװwindowsϵͳ
  • ES 跨订单的详情全局分页 解决
  • 有关于简道云模式选择的思考
  • 详细介绍:80(HTTP默认端口)和8080端口(备用HTTP端口)区别
  • 一加9pro安卓14降级到安卓13记录
  • 【2025-09-08】社交活动