一.管道模式的定义

  管道模式(Pipeline Pattern) 是责任链模式(Chain of Responsibility Pattern)的常用变体之一。在管道模式中,管道扮演着流水线的角色,将数据传递到一个加工处理序列中,数据在每个步骤中被加工处理后,传递到下一个步骤进行加工处理,直到全部步骤处理完毕(比如电子厂里原材料通过流水线工人一个一个加工最后组装成一个完整的电子产品)。
  注:责任链模式只有一个处理器会处理数据,而管道模式是多个处理器会处理

二.什么情况下使用管道模式

  任务代码较为复杂,需求可拆分为多个子步骤,同时需求变更频繁,可能出现在任意位置添加新的子步骤、删除旧的子步骤、交换子步骤顺序,可以考虑使用管道模式。

三.使用管道模式的背景

  最开始做发送律师函系统时,发送律师函的功能,包括:“输入数据校验 -> 根据输入创建律师函 -> 保存律师函到相关 DB 表”总共三个步骤,也不算复杂,所以当时的代码比较简单大概是这样的:

/*** 律师函* @author Administrator**/
@Service
public class LetterServiceImpl implements LetterService {private Logger logger = LoggerFactory.getLogger(this.getClass());@Overridepublic void sendLetter(LetterRequest request) {// 校验数据validatedData(request);//创建律师函createLetter(request);//保存律师函saveLetter(request);}private void saveLetter(LetterRequest request) {logger.info("保存律师函");}private void createLetter(LetterRequest request) {logger.info("创建律师函");}private void validatedData(LetterRequest request) {logger.info("校验律师函数据");}}

  然而好景不长,随着业务不断完善,这块业务逻辑也越来越复杂,律师函模板也增多了,每个模板对应的数据处理方式有一定的区别,同时也需要增加律师函文件电子签章,存储到电子存证平台,后期可能还会有其他一些需求增加比如发送短信,发送邮件等等,基于目前的情况,我们可以在代码里直接封装方法,这样的话暂时可以偷懒一下,但是后期这块逻辑步骤越来越多,对于代码的改动是很大的且不利于维护,因此为了不给自己后期挖坑,同时也为了代码的维护和后期观看决定把这块代码优化一下,优化时打算使用责任链模式,但是责任链模式是一个处理器对其进行处理,而这个是多个步骤的,因此想到了责任链模式之变体“管道模式”。
  注: 直接在源代码上进行修改或增加违背了面向对象的 单一职责原则(一个类应该只发生一个变化)和开闭原则(对扩展开放,对修改关闭)

四.定义一个管道模板上下文

1.定义管道模板上下文 用于处理器处理相应的模板

/*** 律师函管道模板上下文* @author Administrator**/
@Setter
@Getter
public class PipelineTemplateContext {/*** 模板名称*/private String templateName;/*** 获取类名称* @return*/public String getName() {return this.getClass().getSimpleName();}
}
五.构建管道处理器

1.创建一个管道处理器接口,定义一个方法处理数据

/*** 律师函管道处理器* @author Administrator**/
public interface LetterPipelineHandler<T extends PipelineTemplateContext> {/*** 管理处理器处理数据* @param request*/Boolean handler(PipelineTemplateContext context);
}
六.相关处理器具体实现

1.数据校验处理器

/*** 律师函数据校验处理器* @author Administrator**/
@Component
public class ValidatedDataLetterHandler implements LetterPipelineHandler<PipelineTemplateContext> {private Logger logger = LoggerFactory.getLogger(this.getClass());@Overridepublic Boolean handler(PipelineTemplateContext context) {logger.info("数据校验");if(context == null) {try {throw new Exception("数据不能为空");} catch (Exception e) {e.printStackTrace();}}return true;}}

2.表单输入数据处理

/*** 表单输入数据处理器* @author Administrator**/
@Component
public class FormDataLetterHandler implements LetterPipelineHandler<PipelineTemplateContext> {private Logger logger = LoggerFactory.getLogger(this.getClass());@Overridepublic Boolean handler(PipelineTemplateContext context) {logger.info("表单输入数据处理");return true;}}

3.创建律师函

/*** 创建律师函* @author Administrator**/
@Component
public class createLetterHandler implements LetterPipelineHandler<PipelineTemplateContext> {private Logger logger = LoggerFactory.getLogger(this.getClass());@Overridepublic Boolean handler(PipelineTemplateContext context) {logger.info("创建律师函");return true;}}

4.电子签章

/*** 电子签章处理器* @author Administrator**/
@Component
public class SignLetterHandler implements LetterPipelineHandler<PipelineTemplateContext> {private Logger logger = LoggerFactory.getLogger(this.getClass());@Overridepublic Boolean handler(PipelineTemplateContext context) {logger.info("律师函进行电子签章");return true;}}

5.电子存证

/*** 电子存证处理器* @author Administrator**/
@Component
public class DepositCertificateLetterHandler implements LetterPipelineHandler<PipelineTemplateContext> {private Logger logger = LoggerFactory.getLogger(this.getClass());@Overridepublic Boolean handler(PipelineTemplateContext context) {logger.info("律师函存证");return true;}}

6.保存律师函

/*** 保存律师函处理器* @author Administrator**/
@Component
public class SaveLetterHandler implements LetterPipelineHandler<PipelineTemplateContext>{private Logger logger = LoggerFactory.getLogger(this.getClass());@Overridepublic Boolean handler(PipelineTemplateContext context) {logger.info("保存律师函");return true;}}
七.构建管道上下文和处理器映射关系

  管理处理器和管道上下文都定义好了,此时需要把这两个联系起来,在责任链模式中,通常是在处理器中定义下一个处理器,这种方法不能总览全部处理器代码,为了便于总览全部处理器,此时可以通过定义一个路由表来把上下文和处理器想关联起来,路由表里定义一个map,map的key使用PipelineTemplateContext,value 使用一个list集合,集合里存放着处理该上下文的所有处理器,有一个缺点就是如果增加了新的步骤需要在路由表里新增一下。

1.构建路由表

/*** 律师函路由表配置* @author Administrator**/
@Configuration
public class LetterPipelineRouteConfig implements ApplicationContextAware{private ApplicationContext applicationContext;private final static Map<Class<? extends PipelineTemplateContext>,List<Class<? extends LetterPipelineHandler<? extends PipelineTemplateContext>>>> LETTE_RPIPELINE_ROUTE_MAP= new HashMap<>();/*** 系统启动就创建管道上下文与管道处理器映射关系,后续有其他新增或者是删除的处理器可以在此处修改即可*/static {LETTE_RPIPELINE_ROUTE_MAP.put(CommonTemplateContext.class, Arrays.asList(ValidatedDataLetterHandler.class,FormDataLetterHandler.class,createLetterHandler.class,SignLetterHandler.class,DepositCertificateLetterHandler.class,SaveLetterHandler.class));}@Bean("letterPipelineRouteMap")public Map<Class<? extends PipelineTemplateContext>,List<? extends LetterPipelineHandler<? extends PipelineTemplateContext>>> letterPipelineRouteMap(){return LETTE_RPIPELINE_ROUTE_MAP.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, this::toPipeline));}/*** 根据给定的管道中 LetterPipelineHandler 的类型的列表,构建管道*/private List<? extends LetterPipelineHandler<? extends PipelineTemplateContext>> toPipeline(Map.Entry<Class<? extends PipelineTemplateContext>, List<Class<? extends LetterPipelineHandler<? extends PipelineTemplateContext>>>> entry) {return entry.getValue().stream().map(applicationContext::getBean).collect(Collectors.toList());}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}}
八.构建管道执行器

  构建管道执行器。管道执行器 根据传入的上下文数据的类型,找到其对应的管道,然后将上下文数据放入管道中去进行处理。

/*** 律师函管道执行器* @author Administrator**/
@Component
public class LetterPipelineExecutor {private final Logger logger = LoggerFactory.getLogger(this.getClass());/*** 引用 LetterPipelineRouteConfig 中的 letterPipelineRouteMap*/@Autowiredprivate Map<Class<? extends PipelineTemplateContext>,List<? extends LetterPipelineHandler<? super PipelineTemplateContext>>> letterPipelineRouteMap;/***   请记住PECS原则:生产者(Producer)使用extends,消费者(Consumer)使用super。生产者使用extends如果你需要一个列表提供T类型的元素(即你想从列表中读取T类型的元素),你需要把这个列表声明成<? extends T>,比如List<? extends Integer>,因此你不能往该列表中添加任何元素。消费者使用super如果需要一个列表使用T类型的元素(即你想把T类型的元素加入到列表中),你需要把这个列表声明成<? super T>,比如List<? super Integer>,因此你不能保证从中读取到的元素的类型。即是生产者,也是消费者如果一个列表即要生产,又要消费,你不能使用泛型通配符声明列表,比如List<Integer>。注意 ? extends T 与 ?super T  的用法* @param context*/public void executor(PipelineTemplateContext context) {if(context == null) {try {logger.info("参数不能为空");throw new Exception("参数不能为空");} catch (Exception e) {e.printStackTrace();}}Class<? extends PipelineTemplateContext> dataType = context.getClass();Boolean flag = true;List<? extends LetterPipelineHandler<? super PipelineTemplateContext>> pipelineList = letterPipelineRouteMap.get(dataType);if(!CollectionUtils.isEmpty(pipelineList)) {for (LetterPipelineHandler<? super PipelineTemplateContext> handler : pipelineList) {flag = handler.handler(context);if(flag) {break;}}}}}
九.流程代码

1.service层代码

/*** 律师函* @author Administrator**/
public interface LetterService {void sendLetter(LetterRequest request);
}

2.service实现层代码


/*** 律师函* @author Administrator**/
@Service
public class LetterServiceImpl implements LetterService {private Logger logger = LoggerFactory.getLogger(this.getClass());@Autowiredprivate LetterPipelineExecutor letterPipelineExecutor;@Overridepublic void sendLetter(LetterRequest request) {logger.info("律师函业务逻辑层");CommonTemplateContext  context= convert(request);letterPipelineExecutor.executor(context);}private CommonTemplateContext convert(LetterRequest request) {CommonTemplateContext context = new CommonTemplateContext();context.setUserId(request.getUserId());context.setFormInput(request.getFormInput());context.setTemplateName(request.getModelName());return context;}}
十.测试与结果

1.编写简单的controller层代码

@RestController
public class LetterController {@Autowiredprivate LetterService letterService;@PostMapping("letter/settel")public String settel(LetterRequest request) {letterService.sendLetter(request);return "成功";}
}

2.postman测试用例

3.测试结果

十一.扩展

1.可以设想又增加了一些逻辑,比如创建完成以后给用户发送短信,律师函发送短信处理器代码如下

/*** 发送短信通知处理器* @author Administrator**/
@Component
public class SendMsgLetterHandler implements LetterPipelineHandler<CommonTemplateContext> {private Logger logger = LoggerFactory.getLogger(this.getClass());@Overridepublic Boolean handler(CommonTemplateContext context) {logger.info("发送短息通知用户");return true;}}

2.同时也要在路由表那儿把此处理器添加到对应的位置如下图:

3.测试结果,通过下图发现比上一个测试多了一句话,说明成功了,业务逻辑代码没有任何改动,只是在路由表中增加了一个处理器,代码有很好的扩展性。

设计模式-责任链模式变体之管道模式相关推荐

  1. 责任链模式(以及变种管道模式)的应用案例

    目录 一.前言 二.责任链简单使用 场景说明 1.前置代码准备 2.基本接口定义 3.业务节点处理代码 活动时效性检验 活动价格管控 活动风控校验 4.业务代码 5.测试与结果展示 测试代码 结果展示 ...

  2. Java设计模式-责任链模式

    Java设计模式-责任链模式,我想这是值得你了解一下下的啦. 会了就当复习丫,不会来一起来看看吧. 很喜欢一句话:"八小时内谋生活,八小时外谋发展". 如果你也喜欢,让我们一起坚持 ...

  3. 设计模式--责任链模式--Java实现

    设计模式–责任链模式–Java实现 责任链模式–链式数据结构–内存消耗–Java的awt事件一开始用责任链模式后来改为观察者模式–为什么呢?–现在的应用–dom事件–异常处理机制-过滤器链–等等 学习 ...

  4. 理解各种设计模式原则及区别丨浅谈Nginx中核心设计模式-责任链模式丨C++后端开发丨Linux服务器开发丨web服务器

    理解各种设计模式原则及区别丨浅谈Nginx中核心设计模式-责任链模式 1. 开闭.单一职责.里氏替换.接口隔离等设计原则 2. 随处可见的模板方法 3. nginx中核心设计模式 责任链模式 4. 责 ...

  5. java责任链模式审批请假_Java设计模式-责任链模式

    Java设计模式-责任链模式 Java版本:jdk1.8 IDE:IDEA 一 前言 本文介绍责任链模式以及Java下的实现. 二 概念 1.什么是责任链模式 责任链模式(Chain of Respo ...

  6. 设计模式-责任链(职责链)模式及责任链设计模式的应用

    1.什么是责任链设计模式 责任链设计模式主要构成有抽象处理者.具体处理者.客户类,在处理请求的时候,将请求通过客户类发送至处理链路上,这样所有处理对象都有机会处理请求,使发送者与接受者解耦 在责任链模 ...

  7. 设计模式—责任链模式

    原文作者:C语言中文网 原文地址:责任链模式(职责链模式)详解 目录 1.模式的定义与特点 2.模式的结构与实现 在现实生活中,一个事件需要经过多个对象处理是很常见的场景.例如,采购审批流程.请假流程 ...

  8. Java设计模式——责任链模式(职责链模式)详解

    模式的定义与特点 责任链(Chain of Responsibility)模式的定义:为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链:当 ...

  9. 设计模式 -责任链模式

    责任链模式 ​ 在现实生活中,一个事件需要经过多个对象处理是很常见的场景.例如,采购审批流程.请假流程等.公司员工请假,可批假的领导有部门负责人.副总经理.总经理等,但每个领导能批准的天数不同,员工必 ...

最新文章

  1. Java如何优雅的实现时间控制
  2. 苹果微信更新不了最新版本_微信版本又双叒叕更新了,这个功能正式下线
  3. 钱老论逻辑、思维科学、智能机(一)
  4. 解读ASP.NET 5 MVC6系列(17):MVC中的其他新特性
  5. MspEmu 一阶段小结
  6. 不干胶标贴打印模板下载_A4纸打印17*6的不干胶标签打印模板如何设置
  7. 关于使用DataGrid的ButtonColumn,动态添加DataGrid列,实现不定列n个文件的下载功能...
  8. 老李谈JVM内存模型
  9. lunixs 退出mysql_MySQL的基本操作
  10. MongoDB副本集同步原理
  11. php 远程下载大文件,php下载远程文件(支持断点续传,支持超大文件)
  12. 是谁的名字缩写_浅谈女枪 or MF之争 盘点LOL中常见的英雄英文名缩写
  13. RubikFX:用JavaFX 3D解决魔方难题
  14. Linux命令总结(之二)Find
  15. 理解EMM:是更好地管理移动性的关键所在
  16. wps计算机打印双面输出,如何在电脑wps软件内设置双面打印
  17. android 华为 imei,华为手机怎么查看IMEI码?华为手机查询IMEI串号两种方法,华为imei...
  18. 医院建筑综合布线方案特点
  19. SYN FLOOD攻击防范
  20. 加州理工计算机与数学科学,美国加州理工学院计算机科学硕士排名专业攻略权威揭秘...

热门文章

  1. 移柯L620接入电信云平台
  2. 计算机word怎么选中全文,word选定全文快捷键是什么
  3. 服务器和工作站有什么区别?
  4. CH340 +Micro USB转串口 不能识别 不起振问题
  5. shell之系统命令基础
  6. 微信小程序使用echarts实时更新数据以及常见bug
  7. 加号和减号在一起怎么读_怎样看化验单上的加号和减号!
  8. Github博客+腾讯云域名的快捷绑定方案
  9. 思考:你的工作是否有反脆弱性?
  10. ★手机被盗,让小偷欲哭无泪高招+找回你的电话号码+短信陷阱