高级点的 If else: 在Spring中使用责任链

Posted 嗡汤圆

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了高级点的 If else: 在Spring中使用责任链相关的知识,希望对你有一定的参考价值。

高级点的 If else: 在Spring中使用责任链

场景说明

背景

本文的背景: 需要开发一个微信公众号后台,对用户输入的关键词进行相应的回复。
如果采用传统的If else,会发现大量的判断分支和业务实现代码混合在同一个类中,在多人开发时候极易出现代码冲突,切代码难以阅读,同时在分支出现调整需求时也难以随心所欲的调整。因此需要一种模式能够将各个条件分支下的处理逻辑分离开来,并且做到可以随时配置调整。

难点

从上边的场景描述不难想到责任链设计模式,但是特殊的地方在于,我们的业务处理部分需要进行一些查询操作时候用到了Spring管理的一些服务类,需要注入到责任链处理节点中去。一般网络上找到的教程均为手动声明类,并初始化的情况,无法结合Spring的特性。后来找到了一篇文章描述了如何在Spring环境下使用责任链设计模式。链接在此

代码说明

要点

  • 参考网关zuul 的filter配置风格,即包含: doParse, order, parserType三种关键参数,对于多种业务分支可以配置多种责任链
  • Spring支持一次性将一个接口的所有实现类一次性载入进来
  • 借用Ordered 接口 实现责任链的先后排序

关键代码

接口&抽象类

首先需要一个Parser的基础,就做一件事 doParse

/*
 * 处理节点接口
 */
public interface ISupport 
  void doParse(ChainRequestWrapper wrapper) throws Exception;


/*
 * 处理节点虚类
 */
public abstract AbsSupport implements ISupport, Ordered 
  protected AbsSupport support;
  protected boolean shouldParse = false;// 默认不参与处理
  protected String parserType;
  protected int order = Ordered.LOWEST_PRECEDENCE;// 默认最低
  // ...下边一堆 getter setter


/*
 * 处理节点如餐
 */
public class ChainRequestWrapper 
   WxMsgDTO dto;
   boolean shouldParse = true;// 默认载体内参与处理
   // ... getter setter

以上,WxMsgDTO 的具体内容参考微信开发文档,一个XML描述的结构。一般用到了 fromUserName,toUserName,content 等字段。这里的shouldParse在每一个Support节点处理过程当中,一档处理完毕,不再继续往下传递时候,将其设置为false即可。

具体处理节点举例

这种节点继承AbsSupport虚类即可

/*
* 默认处理节点 
*/
public class DefaultUnparseableSupport extends AbsSupport 
  // 构造函数的参数从配置文件获取
  public DefaultUnparseableSupport( 
    @Value("$parser.defaultUnparseableSupport.shouldParse") boolean shouldParse,
    @Value("$parser.defaultUnparseableSupport.order") int order,
    @Value("$parser.defaultUnparseableSupport.parserType") String parserType,
  ) 
  // 以下都在虚类声明了
  this.shouldParse = shouldParse;
  this.order = order;
  this.parserType = parserType;
  

 @Override
 public getOrder() 
   return this.order;
 
 @Override
 public boolean shouldParse(ChainRequestWrapper wrapper) 
   // 节点配置与传递参数配置共同决定
   return wrapper.isShouldParse() && this.shouldParse;
 
 @Override
 public String getParserType() 
   return this.parserType;
 
 @Override
 public void doParse(ChainRequestWrapper wrapper) throws Exception 
   if(shouldParse(wrapper)) 
     WxMsgDTOUtil.swapUserAndSetText(wrapper.getDto(), "默认处理响应");
     wrapper.setShouldParse(false); // 这里设置false即不再参与链条的后续节点的处理
   
 

链条配置

根据上述代码在配置文件中配置 shouldParse, order, parserType等关键配置即可

parser.defaultUnparseableSupport.shouldParse=true
parser.defaultUnparseableSupport.order=9999 #默认节点排最低
parser.defaultUnparseableSupport.parserType=test
# .....

注入&使用

在程序启动时候注入链条,并在需要的地方使用即可

// 注入
@Component
public class SomeParserManager 

  List<AbsSupport> supportList;
  public SomeParserManager(@Autowired List<AbsSupport> supportList) 
    // 这里将所有实现类一次性导入,并仅使用test类型的节点
    this.supportList = supportList.stream().filter(item -> item.getParserType().equals("test")).collect(Collectors.toList());
    Collections.sort(this.supportList,AnnotationAwareOrderCompareator.INSTANCE);
  

  public void doParse(WxMsgDTO dto) 
    // 将微信传入消息封装为wrapper并交给责任链处理
    ChainRequestWrapper wrapper = new ChainRequestWrapper();
    wrapper.setDto(dto);
    wrapper.setShouldParse(true);
    for(AbsSupport support: supportList) 
      support.doParse(wrapper);
    
    // 此时参数传递过程中wrapper内部值会随着处理节点的处理过程修改,最后直接使用wrapper即可
    // toTextXmlString 是按照微信规范返回文字响应的封装方法,各位按需自行编写即可。
    return wrapper.getDto().toTextXmlString();
  

总结

从原来的if else 分支改为责任链的好处在于,多人合作开发期间每个人负责的分支可以独立编写,而不会相互交织,并可以根据配置灵活改变分支的流程和先后顺序。

以上是关于高级点的 If else: 在Spring中使用责任链的主要内容,如果未能解决你的问题,请参考以下文章

在Spring boot项目中使用策略模式消除if-else

函数高级类型名别名if-else 的使用包的使用for循环swich的使用数组的使用

在 Spring Boot 中的 mockito 测试中的 if...else 语句问题

如何使用spring webflux在功能性反应java中编写具有多个if else的复杂代码

在 Spring Boot 中,如何干掉 if else

高级C语言关于if-else的性能问题