责任链模式

责任链模式

  • 概述
  • 优缺点
  • 应用场景
  • Java 代码示例
  • Spring 代码示例
    • 场景一
    • 场景二
    • 场景三

概述

责任链模式(Chain of Responsibility Pattern)是一种行为型设计模式,它通过将请求的发送者和接收者解耦,使多个对象都有机会处理请求。在这个模式中,请求沿着一个处理链依次传递,直到有一个对象能够处理它为止。

责任链模式的核心思想是将请求的发送者和接收者解耦,使得多个对象都有机会处理请求。在责任链模式中,请求会沿着一个处理链依次传递,每个处理者都有机会处理请求,如果一个处理者不能处理请求,则将请求传递给下一个处理者,直到有一个处理者能够处理它。

责任链模式包含以下几个角色:

  • 抽象处理者(Handler):定义了处理请求的接口,通常包含一个指向下一个处理者的引用,用于将请求传递给下一个处理者。
  • 具体处理者(ConcreteHandler):实现了处理请求的接口,具体处理者可以决定是否处理请求,如果不能处理,则将请求传递给下一个处理者。
  • 客户端(Client):创建处理者对象并组成责任链的结构,负责将请求发送给第一个处理者。

优缺点

优点:

  • 责任链模式可以实现请求的发送者和接收者之间的解耦。发送者只需要将请求发送给第一个处理者,无需关心具体是哪个处理者来处理。这样,系统的灵活性大大增强,可以随时增加或修改处理者的顺序。
  • 责任链模式能够避免请求的发送者和接收者之间的紧耦合。每个处理者只需要关心自己负责的请求类型,无需关心其他请求。这样,系统的可维护性也得到了提升。
  • 责任链模式可以灵活地动态添加或删除处理者。我们可以根据实际情况来调整责任链的结构,以满足不同的业务需求。

缺点:

  • 复杂度会明显提升,如果责任链过长或者处理者之间的关系复杂,可能还会导致性能下降和调试困难。

应用场景

责任链模式在许多不同的应用场景中都有广泛的应用。下面列举了一些常见的应用场景:

  • 请求处理链:当一个请求需要经过多个处理步骤或处理者进行处理时,可以使用责任链模式。每个处理者负责一部分逻辑,处理完后可以选择将请求传递给下一个处理者,从而形成一个处理链。
  • 日志记录:在日志系统中,可以使用责任链模式来记录日志。不同的处理者可以负责不同级别的日志记录,例如,一个处理者负责记录错误日志,另一个处理者负责记录调试日志,然后按照链式结构传递日志。
  • 身份验证和权限检查:在身份验证和权限检查系统中,可以使用责任链模式来验证用户的身份和权限。每个处理者可以检查特定的条件,例如用户名和密码的正确性、账户是否锁定等。如果一个处理者无法通过验证,可以将请求传递给下一个处理者。
  • 数据过滤和转换:在数据处理过程中,可以使用责任链模式来进行数据过滤和转换。每个处理者可以根据特定的条件过滤数据或对数据进行转换,然后将处理后的数据传递给下一个处理者。
  • 错误处理和异常处理:在错误处理和异常处理系统中,可以使用责任链模式来处理错误和异常。不同的处理者可以处理不同类型的错误或异常,并根据需要将错误或异常传递给下一个处理者进行进一步处理或记录。

在这里插入图片描述

Java 代码示例

在 Java 中实现责任链模式有多种方式,包括基于接口、基于抽象类、基于注解等。下面将详细介绍基于接口的常见实现方式。

基于接口的实现方式是通过定义一个处理请求的接口,每个处理者实现这个接口,并在自己的实现中决定是否处理请求和传递请求给下一个处理者。

首先,我们定义一个处理请求的接口 Handler 以及请求入参 Request:

public interface Handler {void handleRequest(Request request);
}@Data
public class Request {private String type; 
}

然后,我们创建3个具体的处理者类实现这个接口,在具体处理者类的实现中,首先判断自己是否能够处理请求,如果能够处理,则进行处理;否则将请求传递给下一个处理者。代码如下:

public class ConcreteHandlerA implements Handler {private Handler successor;public void setSuccessor(Handler successor) {this.successor = successor;}public void handleRequest(Request request) {if (request.getType().equals("A")) {// 处理请求的逻辑} else if (successor != null) {successor.handleRequest(request);}}
}public class ConcreteHandlerB implements Handler {private Handler successor;public void setSuccessor(Handler successor) {this.successor = successor;}public void handleRequest(Request request) {if (request.getType().equals("B")) {// 处理请求的逻辑} else if (successor != null) {successor.handleRequest(request);}}
}public class ConcreteHandlerC implements Handler {private Handler successor;public void setSuccessor(Handler successor) {this.successor = successor;}public void handleRequest(Request request) {if (request.getType().equals("C")) {// 处理请求的逻辑} else if (successor != null) {successor.handleRequest(request);}}
}

接下来,我们创建一个客户端类 Client,用于创建处理者对象并组成责任链的结构:

public class Client {public static void main(String[] args) {Handler handlerA = new ConcreteHandlerA();Handler handlerB = new ConcreteHandlerB();Handler handlerC = new ConcreteHandlerC();handlerA.setSuccessor(handlerB);handlerB.setSuccessor(handlerC);// 创建请求并发送给第一个处理者Request request = new Request("A");handlerA.handleRequest(request);}
}

在客户端类中,我们创建了具体的处理者对象,并通过 setSuccessor() 方法将它们组成一个责任链的结构。然后,创建一个请求对象,并将请求发送给第一个处理者。

基于接口的实现方式简单直观,每个处理者只需要实现一个接口即可。但是它的缺点是如果责任链较长,需要创建多个处理者对象,增加了系统的复杂性和资源消耗。下面基于 Spring 框架实现一个高级版的责任链模式。

Spring 代码示例

场景一

在实际开发中,一个请求会在多个处理器之间流转,每个处理器都可以处理请求。

假设我们有一个 Spring 框架开发的订单处理系统,订单需要依次经过订单检查、库存处理、支付处理。如果某个处理环节无法处理订单,将会终止处理并返回错误信息,只有每个处理器都完成了请求处理,这个订单才算法下单成功。

首先,我们定义一个订单类 Order:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Order {private String orderNo;private String paymentMethod;private BigDecimal price;private boolean stockAvailability;private String shippingAddress;
}

然后,我们定义一个抽象订单处理者类 OrderHandler:

public abstract class OrderHandler {public abstract void handleOrder(Order order);
}

接下来,我们创建具体的订单处理者类继承自抽象订单处理者类,实现相应的方法,并注册到 Spring 中,

@Component
public class CheckOrderHandler extends OrderHandler {public void handleOrder(Order order) {if (StringUtils.isBlank(order.getOrderNo())) {throw new RuntimeException("订单编号不能为空");}if (order.getPrice().compareTo(BigDecimal.ONE) <= 0) {throw new RuntimeException("订单金额不能小于等于0");}if (StringUtils.isBlank(order.getShippingAddress())) {throw new RuntimeException("收货地址不能为空");}System.out.println("订单参数检验通过");}
}@Component
public class StockHandler extends OrderHandler {public void handleOrder(Order order) {if (!order.isStockAvailability()) {throw new RuntimeException("订单库存不足");}System.out.println("库存扣减成功");}
}@Component
public class AliPaymentHandler extends OrderHandler {public void handleOrder(Order order) {if (!order.getPaymentMethod().equals("支付宝")) {throw new RuntimeException("不支持支付宝以外的支付方式");}System.out.println("支付宝预下单成功");}
}

在具体订单处理者类的实现中,CheckOrderHandler 负责做订单参数检查、StockHandler 负责做库存扣减、AliPaymentHandler 负责做预下单,每个处理者的逻辑都是相互独立各不不干扰。

最后,我们创建一个订单生产链条 BuildOrderChain ,用于组成责任链的链条处理结构:

@Component
public class BuildOrderChain {@Autowiredprivate AliPaymentHandler aliPaymentHandler;@Autowiredprivate CheckOrderHandler checkOrderHandler;@Autowiredprivate StockHandler stockHandler;List<OrderHandler> list = new ArrayList<>();@PostConstructpublic void init() {// 1. 检查订单参数list.add(checkOrderHandler);// 2. 扣减库存list.add(stockHandler);// 3. 支付宝预下单list.add(aliPaymentHandler);}public void doFilter(Order order) {for (OrderHandler orderHandler : this.list) {orderHandler.handleOrder(order);}}
}

订单生产链条 BuildOrderChain 类中,我们通过 @PostConstruct 注解下的 init() 初始化方法,将具体的订单处理者按代码顺序组成一个责任链的结构。然后通过 doFilter(order) 方法遍历处理者集合依次处理。

运行代码:

@Slf4j
@SpringBootTest
@RunWith(SpringRunner.class)
public class OrderChainTest {@Autowiredprivate BuildOrderChain buildOrderChain;@Testpublic void test() {Order order = new Order("123456", "支付宝",true, "长沙", new BigDecimal("100"));buildOrderChain.doFilter(order);}}-------------------------------
订单参数检验通过
库存扣减成功
支付宝预下单成功

可以看到订单依次经过校验处理器、库存处理器和支付处理器进行处理,直到最后完成整个订单的处理。

在举个例子,假如我们的订单针对的是虚拟不限库存商品,我们不需要进行库存扣减,那我们可以直接新建 VirtualGoodsOrderChain 虚拟商品订单生产链条类,代码如下,

@Component
public class VirtualGoodsOrderChain {@Autowiredprivate AliPaymentHandler aliPaymentHandler;@Autowiredprivate CheckOrderHandler checkOrderHandler;List<OrderHandler> list = new ArrayList<>();@PostConstructpublic void init() {// 1. 检查订单参数list.add(checkOrderHandler);// 2 支付宝预下单list.add(aliPaymentHandler);}public void doFilter(Order order) {for (OrderHandler orderHandler : this.list) {orderHandler.handleOrder(order);}}
}
运行代码:@Test
public void virtualOrderTest() {Order order = new Order("123456", "支付宝", true, "长沙", new BigDecimal("100"));virtualGoodsOrderChain.doFilter(order);
}-------------------------------------------
订单参数检验通过
支付宝预下单成功

总的来说,责任链模式适用于存在多个处理步骤、每个处理步骤具有独立逻辑或条件、需要灵活组合和扩展的场景。通过责任链模式,可以将复杂的处理逻辑拆分为多个独立的处理步骤,并且可以动态地组合和调整处理步骤的顺序,从而提高系统的灵活性和可维护性。希望本文能够帮助读者理解和应用责任链模式,提升软件设计和开发的能力。

场景二

责任链设计模式+工厂模式实现批假流程
1、VacationHandler

public interface VacationHandler {/*** 处理请假* @param day*/void handleApply(Integer day);/*** 处理事情* @param day*/void handle(Integer day);
}

2、AbstractVacationHandler

public abstract class AbstractVacationHandler implements VacationHandler {private VacationHandler nextHandler;@Overridepublic void handle(Integer day) {handleApply(day);if (nextHandler != null) {nextHandler.handle(day);}}public VacationHandler getNextHandler() {return nextHandler;}public void setNextHandler(VacationHandler nextHandler) {this.nextHandler = nextHandler;}
}

3、组长: GrouperVacationHandler

public class GrouperVacationHandler extends AbstractVacationHandler{@Overridepublic void handleApply(Integer day) {if (Objects.nonNull(day)) {System.out.println("组长批准请假"+day+"天");}}
}

4、部长: DepartmenterVacationHandler

public class DepartmenterVacationHandler extends AbstractVacationHandler{@Overridepublic void handleApply(Integer day) {if (Objects.nonNull(day) && day > 3) {System.out.println("部长批准请假"+day+"天");}}
}

5、经理: ManagerVacationHandler

public class ManagerVacationHandler extends AbstractVacationHandler {@Overridepublic void handleApply(Integer day) {if (Objects.nonNull(day) && day > 5) {System.out.println("经理批准请假"+day+"天");}}
}

工产类: VacationHandlerFactory


public class VacationHandlerFactory {/*** 定制VacationHandler的工厂方法* @return*/public static VacationHandler createVacationHandler() {GrouperVacationHandler grouper = new GrouperVacationHandler();DepartmenterVacationHandler departmenter = new DepartmenterVacationHandler();ManagerVacationHandler manager = new ManagerVacationHandler();grouper.setNextHandler(departmenter);departmenter.setNextHandler(manager);return grouper;}
}

测试

 public static void main(String[] args) {VacationHandler vacationHandler = VacationHandlerFactory.createVacationHandler();for (int i = 1; i < 9; i = i+3) {vacationHandler.handle(i);}}

场景三

1、CheckHandler

@Component
public abstract class CheckHandler {private CheckHandler handler;public CheckHandler next() {return this.handler;}public CheckHandler appendHandler(CheckHandler handler) {this.handler = handler;return this;}/*** 审单** @param orderDO 订单主信息* @return*/public abstract void doCheck(OrderDO orderDO);
}

2、地址校验: AddressCheckHandler

@Component("addressCheckCommand")
public class AddressCheckHandler extends CheckHandler {@Overridepublic void doCheck(OrderDO orderDO) {CheckHandler next = super.next();//当前为最后一个规则,结束if (null == next) {return;}next.doCheck(orderDO);}
}

3、 发货快递校验: ExpressCheckHandler

@Component("matchExpressCheckCommand")
public class ExpressCheckHandler extends CheckHandler {@Overridepublic void doCheck(OrderDO orderDO) {//正常单需校验发货快递信息//分销单是交由第三方处理不用校验发货快递CheckHandler next = super.next();//当前为最后一个规则,结束if (null == next) {return;}next.doCheck(orderDO);}
}

4、商品库存校验: ProductStockCheckHandler

@Component("productStockCheckCommand")
public class ProductStockCheckHandler extends CheckHandler {@Overridepublic void doCheck(OrderDO orderDO) {if (orderDO.getOrderType()=="normal"){//正常订单校验商品库存}else {//分销订单校验分销库存}CheckHandler next = super.next();//当前为最后一个规则,结束if (null == next) {return;}next.doCheck(orderDO);}
}

5、OrderController

@RestController
public class OrderController {@Autowiredprivate AddressCheckHandler addressCheckHandler;@Autowiredprivate ExpressCheckHandler expressCheckHandler;@Autowiredprivate ProductStockCheckHandler productStockCheckHandler;@GetMapping("/test")public void doProcess(OrderDO orderDO) {//责任链模式校验CheckHandler checkHandler = addressCheckHandler.appendHandler(expressCheckHandler.appendHandler(productStockCheckHandler));checkHandler.doCheck(orderDO);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/15813.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

RabbitMQ ---- 消息队列

RabbitMQ ---- 消息队列 1. MQ 的相关概念1.1 什么是 MQ1.2 为什么要用 MQ1.3 MQ 的分类1.4 MQ 的选择 2. RabbitMQ2.1 RabbitMQ 的概念2.2 四大核心概念2.3 RabbitMQ 核心部分2.4 各个名词介绍2.5 安装 1. MQ 的相关概念 1.1 什么是 MQ MQ(message queue)&#xff0c;从字面…

ARM架构(寄存器点灯)

文章目录 前言一、LED原理图二、使用寄存器点灯的步骤三、如何操作寄存器四、实际操作1.使能GPIO端口2.将引脚设置为输出模式3.设置输出状态 五、全部代码总结 前言 本篇文章我们来讲解一下如何使用寄存器点亮一个LED灯&#xff0c;一般对于新人来说都是使用HAL库或者标准库来…

Vue3 Diff 算法简易版

背景 学习一下Vue3中的diff算法~ 逻辑概述 这个算法的过程不算太复杂&#xff1a; 同时从新旧列表的头部遍历&#xff0c;找出相同的节点&#xff0c;则patch&#xff0c;否则就停止遍历&#xff1b;同时从新旧列表的尾部遍历&#xff0c;找出相同的节点&#xff0c;则patc…

ASM Java字节码操作框架入门学习 输出Hello World

ASM Java字节码操作框架入门学习 输出Hello World 1.类信息 package org.example;public class Hello {public void say(){System.out.println("hello world");}}查看字节码信息 //动态设置栈大小ClassWriter classWriter new ClassWriter(ClassWriter.COMPUTE_FR…

在vscode中配置git bash终端

将以下配置添加到vscode中的settings.json中 "terminal.integrated.profiles.windows": {"PowerShell": {"source": "PowerShell","icon": "terminal-powershell"},"Command Prompt": {"path"…

Vue3+Vite 项目配置 vue-router,并完成路由模块化

前言 我的技术栈&#xff1a;Vue3 Vite TypeScirpt我的包管理工具&#xff1a;pnpm&#xff08;v8.6.6&#xff09;我的 node.js 版本&#xff1a;v16.14.0 一、安装vue-router pnpm install vue-router二、创建页面 在 /src/views 文件夹下创建 home、login、test文件夹…

【数据挖掘从入门到实战】——专栏导读

目录 1、专栏大纲 &#x1f40b;基础部分 &#x1f40b;实战部分 &#x1f40b;竞赛部分 2、代码附录 数据挖掘专栏&#xff0c;包含基本的数据挖掘算法分析和实战&#xff0c;数据挖掘竞赛干货分享等。数据挖掘是从大规模数据集中发现隐藏模式、关联和知识的过程。它结合…

绝了!阿里大佬的“Redis深度核心笔记“,从基础到源码,全是精华

Redis怎么学习&#xff1f; 我晕了&#xff0c;竟然没人好好回答怎么学习Redis&#xff0c;全都是介绍redis的长文。。。这还让人怎么学。我来分享下我自学Reids看过的资料吧 为什么要学习Redis&#xff1f; Redis 是互联网技术架构在存储系统中使用得最为广泛的中间件&…

【环境配置】Conda报错 requests.exceptions.HTTPError

问题&#xff1a; conda 创建新的虚拟环境时报错 Collecting package metadata (current_repodata.json): done Solving environment: done# >>>>>>>>>>>>>>>>>>>>>> ERROR REPORT <<<<<<…

Installation request for phpoffice/phpspreadsheet

办法 composer update --ignore-platform-reqs

SwiftUI的优缺点

2019年WWDC大会上&#xff0c;苹果在压轴环节向大众宣布了基于Swift语言构建的全新UI框架——SwiftUI&#xff0c;开发者可通过它快速为所有的Apple平台创建美观、动态的应用程序。推荐大量使用struct代替类。 SwiftUI 就是⼀种声明式的构建界面的用户接口工具包。 SwiftUI使用…

会话机制【Cookie 和 Session】,登陆页面的模拟实现

前言 小亭子正在努力的学习编程&#xff0c;接下来将开启JavaEE的学习~~ 分享的文章都是学习的笔记和感悟&#xff0c;如有不妥之处希望大佬们批评指正~~ 同时如果本文对你有帮助的话&#xff0c;烦请点赞关注支持一波, 感激不尽~~ 目录 前言 Cookie 和 Session 是什么 Cookie…