责任链模式
- 概述
- 优缺点
- 应用场景
- 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);}
}