1.背景
项目的背景这样的。有一个导入接口,用户可以通过Excel导入的方式,导入一份Excel文件。文件里面的内容有多列,有些列必须进行参数的校验是否符合规则。大概的任务流程模式如下:
2.常规设计思路
根据上面的流程图,我们很容易就联想到,可以将这个几个方法封装成一个个校验方法,然后一个个调用。伪代码如下:
public class ChainDemo {
public static void main(String[] args) {
CheckObject checkObject = new CheckObject();
ChainDemo chainDemo = new ChainDemo();
chainDemo.checkOrg(checkObject);
chainDemo.checkOrg(checkObject);
chainDemo.checkEmployeeId(checkObject);
chainDemo.checkPhone(checkObject);
chainDemo.checkAccount(checkObject);
}
private void checkOrg(CheckObject checkObject) {
}
private void checkPost(CheckObject checkObject) {
}
private void checkEmployeeId(CheckObject checkObject) {
}
private void checkPhone(CheckObject checkObject) {
}
private void checkAccount(CheckObject checkObject) {
}
}
}
大家应该可以从上面看出来,如果我们后续要校验其他属性,比如说要校验员工的身份证是否符合规则等。就是不断地添加private
方法。这样会带来以下缺点:
- 因为是private方法,因此在Demo类里面的逻辑会越来越多,显得臃肿;
- 缺乏扩展性,诚然我们可以再加一个private方法,但是我们已经修改到了Main方法内部。
3.运用责任链设计模式改造
从上面其实我们可以分析出是一个checkObject
,不断在内部传递验证,这就好比我们平时写代码的时候那些校验链一样。这个时候我们就可以用责任链模式封装起来。
责任链模式有三大要素:
- chain的调用类;
- 方法调用封装的接口;
- 每一个链上节点自己的实现;
public interface IHandler {
boolean handle();
}
public class HandlerA implements IHandler {
@Override
public boolean handle() {
boolean handled = false;
//...
return handled;
}
}
public class HandlerB implements IHandler {
@Override
public boolean handle() {
boolean handled = false;
//...
return handled;
}
}
public class HandlerChain {
private List<IHandler> handlers = new ArrayList<>();
public void addHandler(IHandler handler) {
this.handlers.add(handler);
}
public void handle() {
for (IHandler handler : handlers) {
boolean handled = handler.handle();
if (handled) {
break;
}
}
}
}
// Main方法
public class Application {
public static void main(String[] args) {
HandlerChain chain = new HandlerChain();
chain.addHandler(new HandlerA());
chain.addHandler(new HandlerB());
chain.handle();
}
}
4.项目中的实际案例
从第三点我们知道了基本的使用。但是我们现在一般Java项目和Spring分不开,实际应用还得和Spring结合起来。
4.1 定义通用接口
public interface IBatchUserInfoCheckHandler {
/**
* 处理校验请求
* @param checkUserBatchInfoDTO 参数
* @return 返回是否校验成功
*/
void handle(CheckUserBatchInfoDTO checkUserBatchInfoDTO);
4.1 在链中传递的封装类
public class CheckUserBatchInfoDTO {
// 各种属性
}
4.2 实现IBatchUserInfoCheckHandler 的各种Handler
由于都是大同小异,因此这里只列出几个实现的接口
@Service
// @Order 注解可以让我们控制注入的顺序,1就代表第一个1,优先级最高
@Order(1)
public class CheckOrgHandler implements IBatchUserInfoCheckHandler {
@Override
public void handle(CheckUserBatchInfoDTO checkUserBatchInfoDTO) {
// 处理校验组织架构
}
}
@Service
@Order(2)
public class CheckPositionHandler implements IBatchUserInfoCheckHandler{
@Override
public void handle(CheckUserBatchInfoDTO checkUserBatchInfoDTO) {
// 处理校验岗位的逻辑
}
}
4.3 一个Chain类
@Component
@Slf4j
public class BatchUserInfoCheckHandlerChain {
// Spring会找到所有实现了IBatchUserInfoCheckHandler接口的handler依次注入
@Autowired
private List<IBatchUserInfoCheckHandler> handlerList;
public void handle(CheckUserBatchInfoDTO checkUserBatchInfoDTO) {
if (CollectionUtils.isNotEmpty(handlerList)) {
for (IBatchUserInfoCheckHandler handler : handlerList) {
handler.handle(checkUserBatchInfoDTO);
}
}
}
}
4.4 依赖注入使用
@Service
public class TestService {
@Resource
private BatchUserInfoCheckHandlerChain chain;
public void checkValue() {
CheckUserBatchInfoDTO checkObject = new CheckUserBatchInfoDTO ();
chain.handle(checkObject );
}
}
从上面的时候我们可以看出,如果我们后面想要扩展的话,非常的简单,只需要新建一个类实现IBatchUserInfoCheckHandler
,设置好@Order的顺序,以前调用的地方完全不用改。非常符合设计原则。假设以后我们需要修改以前的代码,比如说校验组织架构的逻辑改了,我们直接就可以在CheckOrgHandler里面进行修改,并不影响以前的调用方式。
结语
如果我们项目中存在这种需要链式调用的,我们不妨使用上责任链
模式优化一把,可以使我们的代码符合设计原则,让代码看上去更简洁。