最近学习了责任链模式
2019独角兽企业重金招聘Python工程师标准>>>
前言
来菜鸟这个大家庭10个月了,总得来说比较融入了环境,同时在忙碌的工作中也深感技术积累不够,在优秀的人身边工作必须更加花时间去提升自己的技术能力、技术视野,所以开一个系列文章,标题就轻松一点叫做最近学习了XXX吧,记录一下自己的学习心得。
由于最近想对系统进行一个小改造,想到使用责任链模式会非常适合,因此就系统地学习总结了一下责任链模式,分享给大家。
责任链模式的定义与特点
责任链模式的定义:使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系,将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理他为止。
标准的责任链模式,个人总结下来有如下几个特点:
- 链上的每个对象都有机会处理请求
- 链上的每个对象都持有下一个要处理对象的引用
- 链上的某个对象无法处理当前请求,那么它会把相同的请求传给下一个对象
用一张图表示以下使用了责任链模式之后的架构:
也就是说,责任链模式满足了请求发送者与请求处理者之间的松耦合,抽象非核心的部分,以链式调用的方式对请求对象进行处理。
这么说不明白?那么下面通过实际例子让你明白。
不使用责任链模式
为什么要使用责任链模式,那么我们得知道不使用责任链模式有什么坏处,然后通过使用责任链模式如何将代码优化。
现在有一个场景:小明要去上学,妈妈给小明列了一些上学前需要做的清单(洗头、吃早饭、洗脸),小明必须按照妈妈的要求,把清单上打钩的事情做完了才可以上学。
首先我们定义一个准备列表PreparationList:
1 public class PreparationList {2 3 /**4 * 是否洗脸5 */6 private boolean washFace;7 8 /**9 * 是否洗头 10 */ 11 private boolean washHair; 12 13 /** 14 * 是否吃早餐 15 */ 16 private boolean haveBreakfast; 17 18 public boolean isWashFace() { 19 return washFace; 20 } 21 22 public void setWashFace(boolean washFace) { 23 this.washFace = washFace; 24 } 25 26 public boolean isWashHair() { 27 return washHair; 28 } 29 30 public void setWashHair(boolean washHair) { 31 this.washHair = washHair; 32 } 33 34 public boolean isHaveBreakfast() { 35 return haveBreakfast; 36 } 37 38 public void setHaveBreakfast(boolean haveBreakfast) { 39 this.haveBreakfast = haveBreakfast; 40 } 41 42 @Override 43 public String toString() { 44 return "ThingList [washFace=" + washFace + ", washHair=" + washHair + ", haveBreakfast=" + haveBreakfast + "]"; 45 } 46 47 }
定义了三件事情:洗头、洗脸、吃早餐。
接着定义一个学习类,按妈妈要求,把妈妈要求的事情做完了再去上学:
1 public class Study {2 3 public void study(PreparationList preparationList) {4 if (preparationList.isWashHair()) {5 System.out.println("洗脸");6 }7 if (preparationList.isWashHair()) {8 System.out.println("洗头");9 } 10 if (preparationList.isHaveBreakfast()) { 11 System.out.println("吃早餐"); 12 } 13 14 System.out.println("我可以去上学了!"); 15 } 16 17 }
这个例子实现了我们的需求,但是不够优雅,我们的主流程是学习,但是把要准备做的事情这些动作耦合在学习中,这样有两个问题:
- PreparationList中增加一件事情的时候,比如增加化妆、打扫房间,必须修改study方法进行适配
- 当这些事情的顺序需要发生变化的时候,必须修改study方法,比如先洗头再洗脸,那么7~9行的代码必须和4~6行的代码互换位置
最糟糕的写法,只是为了满足功能罢了,违背开闭原则,即当我们扩展功能的时候需要去修改主流程,无法做到对修改关闭、对扩展开放。
使用责任链模式
接着看一下使用责任链模式的写法,既然责任链模式的特点是“链上的每个对象都持有下一个对象的引用”,那么我们就这么做。
先抽象出一个AbstractPrepareFilter:
1 public abstract class AbstractPrepareFilter {2 3 private AbstractPrepareFilter nextPrepareFilter;4 5 public AbstractPrepareFilter(AbstractPrepareFilter nextPrepareFilter) {6 this.nextPrepareFilter = nextPrepareFilter;7 }8 9 public void doFilter(PreparationList preparationList, Study study) { 10 prepare(preparationList); 11 12 if (nextPrepareFilter == null) { 13 study.study(); 14 } else { 15 nextPrepareFilter.doFilter(preparationList, study); 16 } 17 } 18 19 public abstract void prepare(PreparationList preparationList); 20 21 }
留一个抽象方法prepare给子类去实现,在抽象类中持有下一个对象的引用nextPrepareFilter,如果有,则执行;如果没有表示链上所有对象都执行完毕,执行Study类的study()方法:
1 public class Study {2 3 public void study() {4 System.out.println("学习");5 }6 7 }
接着我们实现AbstractPrepareList,就比较简单了,首先是洗头:
1 public class WashFaceFilter extends AbstractPrepareFilter {2 3 public WashFaceFilter(AbstractPrepareFilter nextPrepareFilter) {4 super(nextPrepareFilter);5 }6 7 @Override8 public void prepare(PreparationList preparationList) {9 if (preparationList.isWashFace()) { 10 System.out.println("洗脸"); 11 } 12 13 } 14 15 }
接着洗脸:
1 public class WashHairFilter extends AbstractPrepareFilter {2 3 public WashHairFilter(AbstractPrepareFilter nextPrepareFilter) {4 super(nextPrepareFilter);5 }6 7 @Override8 public void prepare(PreparationList preparationList) {9 if (preparationList.isWashHair()) { 10 System.out.println("洗头"); 11 } 12 13 } 14 15 }
最后吃早餐:
1 public class HaveBreakfastFilter extends AbstractPrepareFilter {2 3 public HaveBreakfastFilter(AbstractPrepareFilter nextPrepareFilter) {4 super(nextPrepareFilter);5 }6 7 @Override8 public void prepare(PreparationList preparationList) {9 if (preparationList.isHaveBreakfast()) { 10 System.out.println("吃早餐"); 11 } 12 13 } 14 15 }
最后我们看一下调用方如何编写:
1 @Test2 public void testResponsibility() {3 PreparationList preparationList = new PreparationList();4 preparationList.setWashFace(true);5 preparationList.setWashHair(false);6 preparationList.setHaveBreakfast(true);7 8 Study study = new Study();9 10 AbstractPrepareFilter haveBreakfastFilter = new HaveBreakfastFilter(null); 11 AbstractPrepareFilter washFaceFilter = new WashFaceFilter(haveBreakfastFilter); 12 AbstractPrepareFilter washHairFilter = new WashHairFilter(washFaceFilter); 13 14 washHairFilter.doFilter(preparationList, study); 15 }
至此使用责任链模式修改这段逻辑完成,看到我们完成了学习与准备工作之间的解耦,即核心的事情我们是要学习,此时无论加多少准备工作,都不需要修改study方法,只需要修改调用方即可。
但是这种写法好吗?个人认为这种写法虽然符合开闭原则,但是两个明显的缺点对客户端并不友好:
- 增加、减少责任链对象,需要修改客户端代码,即比如我这边想要增加一个打扫屋子的操作,那么testResponsibility()方法需要改动
- AbstractPrepareFilter washFaceFilter = new WashFaceFilter(haveBreakfastFilter)这种调用方式不够优雅,客户端需要思考一下,到底真正调用的时候调用三个Filter中的哪个Filter
为此,我们来个终极版的、升级版的责任链模式。
升级版责任链模式
上面我们写了一个责任链模式,这种是一种初级的符合责任链模式的写法,最后也写了,这种写法是有明显的缺点的,那么接着我们看一下升级版的责任链模式如何写,解决上述问题。
以下的写法也是Servlet的实现方式,首先还是抽象一个Filter:
1 public interface StudyPrepareFilter {2 3 public void doFilter(PreparationList preparationList, FilterChain filterChain);4 5 }
注意这里多了一个FilterChain,也就是责任链,是用于串起所有的责任对象的,它也是StudyPrepareFilter的一个子类:
1 public class FilterChain implements StudyPrepareFilter {2 3 private int pos = 0;4 5 private Study study;6 7 private List<StudyPrepareFilter> studyPrepareFilterList;8 9 public FilterChain(Study study) { 10 this.study = study; 11 } 12 13 public void addFilter(StudyPrepareFilter studyPrepareFilter) { 14 if (studyPrepareFilterList == null) { 15 studyPrepareFilterList = new ArrayList<StudyPrepareFilter>(); 16 } 17 18 studyPrepareFilterList.add(studyPrepareFilter); 19 } 20 21 @Override 22 public void doFilter(PreparationList thingList, FilterChain filterChain) { 23 // 所有过滤器执行完毕 24 if (pos == studyPrepareFilterList.size()) { 25 study.study(); 26 } 27 28 studyPrepareFilterList.get(pos++).doFilter(thingList, filterChain); 29 } 30 31 }
即这里有一个计数器,假设所有的StudyPrepareFilter没有调用完毕,那么调用下一个,否则执行Study的study()方法。
接着就比较简单了,实现StudyPrepareFilter类即可,首先还是洗头:
1 public class WashHairFilter implements StudyPrepareFilter {2 3 @Override4 public void doFilter(PreparationList preparationList, FilterChain filterChain) {5 if (preparationList.isWashHair()) {6 System.out.println("洗完头发");7 }8 9 filterChain.doFilter(preparationList, filterChain); 10 } 11 12 }
注意,这里每个实现类需要显式地调用filterChain的doFilter方法。洗脸:
1 public class WashFaceFilter implements StudyPrepareFilter {2 3 @Override4 public void doFilter(PreparationList preparationList, FilterChain filterChain) {5 if (preparationList.isWashFace()) {6 System.out.println("洗完脸");7 }8 9 filterChain.doFilter(preparationList, filterChain); 10 } 11 12 }
吃早饭:
1 public class HaveBreakfastFilter implements StudyPrepareFilter {2 3 @Override4 public void doFilter(PreparationList preparationList, FilterChain filterChain) {5 if (preparationList.isHaveBreakfast()) {6 System.out.println("吃完早饭");7 }8 9 filterChain.doFilter(preparationList, filterChain); 10 } 11 12 }
最后看一下调用方:
1 @Test2 public void testResponsibilityAdvance() {3 PreparationList preparationList = new PreparationList();4 preparationList.setWashFace(true);5 preparationList.setWashHair(false);6 preparationList.setHaveBreakfast(true);7 8 Study study = new Study();9 10 StudyPrepareFilter washFaceFilter = new WashFaceFilter(); 11 StudyPrepareFilter washHairFilter = new WashHairFilter(); 12 StudyPrepareFilter haveBreakfastFilter = new HaveBreakfastFilter(); 13 14 FilterChain filterChain = new FilterChain(study); 15 filterChain.addFilter(washFaceFilter); 16 filterChain.addFilter(washHairFilter); 17 filterChain.addFilter(haveBreakfastFilter); 18 19 filterChain.doFilter(preparationList, filterChain); 20 }
完美解决第一版责任链模式存在的问题,至此增加、修改责任对象客户端调用代码都不需要再改动。
有的人可能会问,你这个增加、减少责任对象,testResponsibilityAdvance()方法,不是还得addFilter,或者删除一行吗?我们回想一下,Servlet我们增加或减少Filter需要改动什么代码吗?不用,我们需要改动的只是web.xml而已。同样的道理,FilterChain里面有studyPrepareFilterList,我们完全可以把FilterChain做成一个Spring Bean,所有的Filter具体实现类也都是Spring Bean,注入studyPrepareFilterList就好了,伪代码为:
1 <bean id="filterChain" class="xxx.xxx.xxx.FilterChain"> 2 <property name="studyPrepareFilterList"> 3 <list> 4 <ref bean="washFaceFilter" /> 5 <ref bean="washHairFilter" /> 6 <ref bean="haveBreakfastFilter" /> 7 </list> 8 </property> 9 </bean>
这样是不是完美解决了问题?我们新增、减少Filter,或者修改Filter顺序,只需要修改.xml文件即可,不仅核心逻辑符合开闭原则,调用方也符合开闭原则。
责任链模式的使用场景
这个就不多说了,最典型的就是Servlet中的Filter,有了上面的分析,大家应该也可以理解Servlet中责任链模式的工作原理了,然后为什么一个一个的Filter需要配置在web.xml中。
责任链模式的结构
想想看,好像责任链模式也没有什么太复杂的结构,将责任抽象,实现责任接口,客户端发起调用,网上找了一张图表示一下:
责任链模式的优点及使用场景
最后说说责任链模式的优点吧,大致有以下几点:
- 实现了请求发送者与请求处理者之间的松耦合
- 可动态添加责任对象、删除责任对象、改变责任对象顺序,非常灵活
- 每个责任对象专注于做自己的事情,职责明确
什么时候需要用责任链模式?这个问题我是这么想的:系统设计的时候,注意区分主次就好,即哪部分是核心流程,哪部分是辅助流程,辅助流程是否有N多if...if...if...的场景,如果是且每个if都有一个统一的抽象,那么抽象辅助流程,把每个if作为一个责任对象进行链式调用,优雅实现,易复用可扩展。
==================================================================================
我不能保证写的每个地方都是对的,但是至少能保证不复制、不黏贴,保证每一句话、每一行代码都经过了认真的推敲、仔细的斟酌。每一篇文章的背后,希望都能看到自己对于技术、对于生活的态度。
我相信乔布斯说的,只有那些疯狂到认为自己可以改变世界的人才能真正地改变世界。面对压力,我可以挑灯夜战、不眠不休;面对困难,我愿意迎难而上、永不退缩。
其实我想说的是,我只是一个程序员,这就是我现在纯粹人生的全部。
==================================================================================
转载于:https://my.oschina.net/xiaowangqiongyou/blog/3048250
最近学习了责任链模式相关推荐
- 设计模式学习笔记——责任链(Chain of Responsibility)模式
设计模式学习笔记--责任链(Chain of Responsibility)模式 @(设计模式)[设计模式, 责任链模式, chain of responsibility] 设计模式学习笔记责任链Ch ...
- 轻松学习Java设计模式之责任链模式
我们的态度是:每天进步一点点,理想终会被实现. 前言 设计模式,可能很多人都是看到代码知道怎么回事,但是离开代码再让其说出来,估计就有点含糊其词了,包括我自己在内.Android中其实用到的设计模式也 ...
- 设计模式学习(十四) 责任链模式 COR
1.责任链模式的定义 有些事情回武汉了几天,导致近几天没有学习= =,很是愧疚 责任链模式的英文名比较长,是Chain of Responsibility,简称为COR模式.其主要任务是构造一系列担 ...
- 设计模式之责任链模式在王者荣耀中的应用,学习到了!
点击上方蓝色字体,选择"标星公众号" 优质文章,第一时间送达 关注公众号后台回复pay或mall获取实战项目资料+视频 作者:荣仔!最靓的仔! 来源:https://blog.cs ...
- 责任链模式——HeadFirst设计模式学习笔记
责任链模式:使一个以上的对象都有机会能够处理某个请求 特点: 链中的每个对象包含它下一个对象的引用和对事件的处理方法.请求在这个链上传递,直到链上的某一个对象决定处理此请求 发出这个请求的客户端并不知 ...
- Java设计模式学习总结(15)——行为型模式之责任链模式
责任链模式 顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链.这种模式给予请求的类型,对请求的发送者和接收者进行解耦.这种类型的设计 ...
- 设计模式学习笔记(六:责任链模式)
1.1概述 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系.将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止.这就是责任链模式. 责任链模式是使用多个对象 ...
- 设计模式之责任链模式学习
目录 一.责任链模式解释 二.介绍 意图 主要解决 何时使用 如何解决 关键代码 应用实例 三.优点 四.缺点 五.使用场景 六.实现1--抽象类实现 七.实现2--接口实现 一.责任链模式解释 顾名 ...
- 折腾Java设计模式之责任链模式
责任链模式 顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链.这种模式给予请求的类型,对请求的发送者和接收者进行解耦.这种类型的设计 ...
最新文章
- ssh框架实现数据库_自顶向下介绍SSH及其如何实现安全的数据共享
- 2.11 总结-深度学习第三课《结构化机器学习项目》-Stanford吴恩达教授
- 【中级软考】段页式存储是什么?
- 【Qt】数据库实战(三)
- 网站架构相关PPT、文章整理
- final、finally与finalize的区别
- 【渝粤题库】国家开放大学2021春2444酒店管理概论答案
- 《SEO的艺术(原书第2版)》——第1章 搜索:反映认知、连接商务
- 马化腾王健林一起逛街 买了一杯茉莉茶却忘了拿走
- html 中rem是什么单位,了解并使用CSS中的rem单位
- .net知识系列之五:(基本控件和数据绑定控件)
- 数据仓库与数据挖掘论文
- linux 小度 驱动_小度 WiFi 与 Windows 和 Linux
- 华为转正答辩ppt范文_华为新员工转正答辩
- 打砖块python游戏源代码_python制作一个打砖块小游戏
- (转帖)Redis的LRU和LFU区别
- 字节码文件的内部结构之谜
- rtx3050和rtx3050ti区别 rtx3050和rtx3050ti显卡什么水平
- LSTM模型预测sin函数详解
- 最新斩获2022字节暑期实习生 一二三面(已过|新鲜面经)
热门文章
- Java中classpath配置
- 很好的Markdown开源库
- Atitit JAVA p2p设计与总结 JXTA 2
- Chapter 1 Securing Your Server and Network(9):使用Kerberos用于身份验证
- PHPUnit单元测试 - 我看过的PHP开源框架
- What are some time-saving tips that every Linux us
- sqlserver 新建只读权限用户
- winformDatagridview无法编辑的问题
- 结合项目实例 回顾传统设计模式(九)迭代器模式
- 用DIV+Javascript实现标签功能