从一张关于Excel文件导入的故事卡谈起 在9月份的时候,还有一件事让我觉得很有收获
在领到一张关于excel导入的故事卡时候,我像往常一样开始找文军老师 pair coding
故事卡的内容主要是要将excel的内容进行批量导入,并且导入后还要进行判空校验、格式校验、业务场景校验(用户是否存在、用户A、B是否存在关系)、重复数据校验以及最后的组装等操作
区别于以往pair时我们先写测试的惯例,文军老师那天突然问我
“你知不知道责任链?”
“学设计模式的时候好像听说过,但是也没有实践过”
文军老师看出了我不会的尴尬,随即开始讲起了什么是责任链
责任链设计模式 从wiki上的介绍来看,责任链主要是
In object-oriented design, the chain-of-responsibility pattern is a behavioral design pattern consisting of a source of command objects and a series of processing objects. Each processing object contains logic that defines the types of command objects that it can handle; the rest are passed to the next processing object in the chain. A mechanism also exists for adding new processing objects to the end of this chain.
概括的来说,其实是这三点内容:
1. 有一个待处理的请求
1. 有一系列的对象可以对这个请求进行处理
1. 每一个对象都有自己的上下文,彼此互不干涉。处理完后,交由下一个对象来处理。
我们还是先以一个例子来进行说明
客户输入一个request,我们要对这个request进行处理,并将处理好的结果打印出来
首先定义一个Request对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import java.util.List;public class Request { private List<String> operations; public Request (List<String> operation) { this .operations = operation; } public List<String> getOperations () { return operations; } public void setOperation (String operation) { this .operations.add(operation); } }
然后我们不择手段的先写一个相应的处理实现方法,就写在main方法里面(为了方便演示,具体内容就省略了😂)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package ChainOfReposibility;import java.util.ArrayList;import java.util.Collections;public class Main { public static void main (String[] args) { Request request = new Request (new ArrayList <>(Collections.singletonList("This is a request" ))); request.setOperation("request is not null" ); request.setOperation("request is legal" ); request.setOperation("command build completed" ); request.getOperations().forEach(System.out::println); } }
得到的结果:
1 2 3 4 5 > output: This is a request request is not null request is legal command build completed
这样写的好处是简单直接,但坏处显而易见。违反了SOLID 原则上的单一职责和开闭和原则,很难进行扩展
于是我们可以尝试将不同的处理步骤进行抽取方法,重构后得到下面的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package ChainOfReposibility;import java.util.ArrayList;import java.util.Collections;public class Main { public static void main (String[] args) { Request request = new Request (new ArrayList <>(Collections.singletonList("This is a request" ))); checkRequestIsNull(request); checkRequestlegal(request); buildRequestToCommand(request); request.getOperations().stream() .forEach(System.out::println); } private static void buildRequestToCommand (Request request) { request.setOperation("command build completed" ); } private static void checkRequestlegal (Request request) { request.setOperation("request is legal" ); } private static void checkRequestIsNull (Request request) { request.setOperation("request is not null" ); } }
此时,开闭合原则其实已经解决了,需要加任何扩展或者删除任何方法都变的比之前更加灵活了
但是我们仔细观察后可以发现,其实这里面每个处理Request的对象都只做了一件事情,就是处理本身而已
那我们能不能规定一个接口,并将这些方法分别Delegate到各自的类中去并且让这些类都实现我们定义的接口呢?
于是,责任链的原型就被我们重构出来了:
⚠️Warning
以下内容存在分歧,责任链的定义和解释建议参考:chain-of-responsibility
1 2 3 4 5 package ChainOfResponsibility;public interface Handler <T> { void process (T request) ; }
三个类,举其中一个做例子:
1 2 3 4 5 6 7 8 9 10 package ChainOfResponsibility;public class CheckRequestIsNullHandler implements Handler <Request> { @Override public void process (Request request) { request.setOperation("request is not null" ); } }
最后,再新建一个类用来触发执行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package ChainOfResponsibility;import java.util.ArrayList;import java.util.List;public class ChainOfResponsibility implements Handler <Request> { private List<Handler> chains = new ArrayList <>(); public ChainOfResponsibility nextHandler (Handler handler) { chains.add(handler); return this ; } @Override public void process (Request request) { chains.forEach(handler -> handler.process(request)); } }
使用责任链后的Main类如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package ChainOfResponsibility;import java.util.ArrayList;import java.util.Collections;public class Main { public static void main (String[] args) { Request request = new Request (new ArrayList <>(Collections.singletonList("This is a request" ))); ChainOfResponsibility chainOfResponsibility = new ChainOfResponsibility (); chainOfResponsibility .nextHandler(new CheckRequestIsNullHandler ()) .nextHandler(new CheckRequestLegalHandler ()) .nextHandler(new BuildRequestToCommandHandler ()); chainOfResponsibility.process(request); request.getOperations().forEach(System.out::println); } }
模型如下图所示:
结论 使用责任链模式的好处明显能够看出,比如
满足SOLID 原则
发送请求方和接受请求方解耦
易于扩展(尤其是结合范型后,扩展性大大增强)
但是,其缺点也很明显,比如:
链式数据结构必然会牺牲一定的效率
不保证被接受,正因为上述的解耦的优点,同时也会产生新的问题,即请求没有明确的接收者,那就不保证一定会被处理。
由于责任链的链式结构,使得每一个节点对象处理完后都只能被下一个节点处理,并不能根据条件判断进行多个候选节点的分发。不过,针对这个问题,衍生出来的责任树 方法提供了一种解决方案,感兴趣的同学可以看看。
同样由于每一个处理对象只负责自己职责的事情,对之前和之后的处理并不关心,使得之后的问题排查变得更加困难。
最后,依然感谢团队TL成哥和Coach文军老师提供的各种帮助。以及团队中其他小伙伴的支持,比心♥️
版权声明: 一、全文版权归本作者所有,其他人不得用于商业活动 二、如需转载文章,务必全篇转载,不得进行未经本人同意的修改 三、如需转载文章,必须注明出处和作者,以方便其他读者溯源。