开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 3 天,点击查看活动详情
前言
上篇讲到设计模式之模版模式加载类,这一篇主要是以我在开发过程中,写业务代码所用到的另外一种设计模式—责任链模式,它与策略模式有点类似,可以避免我们少写一些if…else判断,还有switch语句这种。相信大家有时候看到,代码里面嵌套的一坨又一坨的if…else多少会有些头大,这时候就需要我们学习的一些设计模式来使我们代码的可读性、可扩展性、可维护性变强,所谓前人挖坑后人填坑,前人造福后人享福,我们一定要争取做造福的人。
业务场景
在订单系统里,由于业务的原因订单会有多种类型,比如有订单A、订单B、订单C,我们需要根据不同的订单类型,对接下游结算服务生成不同的计费单,并且订单类型都属于每一个节点,一个节点的业务逻辑走完才会接着走到下一节点,每种类型的订单就会有组装数据、对接结算服务接口、对接成功后会去更新自己的主表。
如果按常规的写法去实现这个需求,那么就要分别写三个接口,分别为订单类型为A的、B的、C的,并且都实现对应的三个方法,然后在调用的时候通过用if判断类型,最终去调用哪个service。 这样来做的话我们接口的耦合度就非常低,如果业务有改动,那么这三个接口的方法都要进行改动,这样改动量就有点大了,同时测试的工作量也会增长。 随着我们业务的拓展,会有很多订单类型,如果每加一种订单类型就去写一个接口的话,那样项目的体积会非常大,同时也会有很多重复的代码逻辑,整个项目会变得非常臃肿。
责任链模式
有链的概念说明,链中是有序的,或者说是有多个节点的,每个节点处理完成后会传递给下一个节点,一环扣一环的。
在责任链中其实只包含着两种角色:
- Handler:公共的处理类,解藕了所有请求的公共方法,将公共方法进行抽象。
- Next(下一个处理者):在链表的数据结构也是有对应的next节点,也是一个独立的对象,完成着对应节点所要做的事情,让请求能够有效的传递到下一个节点。
具体实现
OrderHandler抽象订单公共处理类
@Data
public abstract class OrderHandler {
/**
* 下一个处理类
*/
private OrderHandler next;
/**
* 获取数据
*/
public abstract void getOrderData();
/**
* 节点传递方法
*/
public void getOrderDataProcess() {
this.getOrderData();
if (null != next) {
next.getOrderDataProcess();
}
}
/**
* 请求接口
*/
public abstract void getRequestClient();
/**
* 请求接口节点传递方法
*/
public void getRequestClientProcess() {
this.getRequestClient();
if (null != next) {
next.getRequestClientProcess();
}
}
/**
* 更新结果
*/
public abstract void updateResult();
/**
* 传递下一节点进行更更新
*/
public void updateResultProcess() {
this.updateResult();
if (null != next) {
next.updateResultProcess();
}
}
}
OrderHandlerClient 责任链阶段处理类
@Component
@AllArgsConstructor
public class OrderHandlerClient {
private OrderTypeAHandler orderTypeAHandler;
private OrderTypeBHandler orderTypeBHandler;
private OrderTypeCHandler orderTypeCHandler;
/**
* 处理类节点链路 顺序为 A->B->C
* @return
*/
public OrderHandler getInitFirstHandler(){
orderTypeAHandler.setNext(orderTypeBHandler);
orderTypeBHandler.setNext(orderTypeCHandler);
return orderTypeAHandler;
}
}
OrderTypeAHandler 订单类型为A的链路对象
@Component
@Slf4j
public class OrderTypeAHandler extends OrderHandler{
@Autowired
private RequestUtil requestUtil;
@Autowired
private ResultUtil resultUtil;
@Override
public void getOrderData() {
log.info("订单类型为A--------组装数据");
}
@Override
public void getRequestClient() {
requestUtil.request(OrderA.class);
}
@Override
public void updateResult() {
resultUtil.updateResult(OrderA.class);
}
}
这里只写一个,类型为B、C的同A一样,继承OrderHandler抽象类即可。
ResultUtil更新结果泛型类
@Slf4j
@Component
public class ResultUtil<T,K> {
//范型类,可以通过传不同的类,以及数据更新的mapper
// 进行反射 ,来对不同订单类的表进行更新
public void updateResult(Class<T> tClass){
log.info("公共更新的结果方法~~~~~~~~~·");
//反射获取对应的处理类
T t = ReflectUtil.newInstance(tClass);
log.info("处理类:"+t.getClass().getName());
}
}
RequestUtil请求接口公共工具类
@Slf4j
@Component
public class RequestUtil<T,K>{
public void request(Class<T> tClass){
log.info("公共调用接口的方法~~~~~~~~~·");
//反射获取对应的处理类
T t = ReflectUtil.newInstance(tClass);
log.info("处理类:"+t.getClass().getName());
}
}
测试类
@RestController
@AllArgsConstructor
public class ChainTest {
private OrderHandlerClient orderHandlerClient;
@GetMapping("/test")
public void chainTestMethod(){
OrderHandler orderFeeHandler = orderHandlerClient.getInitFirstHandler();
//获取数据
orderFeeHandler.getOrderDataProcess();
//调用接口
orderFeeHandler.getRequestClientProcess();
//更新结果
orderFeeHandler.updateResultProcess();
}
}
测试结果
由此可见整个处理链路,按照我在OrderHandlerClient链路处理类的节点顺序在执行,并且在某个节点执行了业务逻辑方法,包括获取数据、调用接口、更新数据。
总结
以上演示了整个责任链的具体实现,可以看到整个调用链是不是非常清晰呢,代码的结构也是相对清晰的,优点显而易见。但是其实也是有缺点的,这里包括可能某个节点的处理时间过长,会影响整体的性能,还有就是节点不要循环引用,从而出现死循环,造成服务挂掉。合理使用设计模式,使我们的代码事半功倍。