最近学习了责任链模式

前言

来菜鸟这个大家庭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     @Override
 8     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     @Override
 8     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     @Override
 8     public void prepare(PreparationList preparationList) {
 9         if (preparationList.isHaveBreakfast()) {
10             System.out.println("吃早餐");
11         }
12
13     }
14
15 }

最后我们看一下调用方如何编写:

 1 @Test
 2 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     @Override
 4     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     @Override
 4     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     @Override
 4     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 @Test
 2 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://www.cnblogs.com/xrq730/p/10633761.html

时间: 2024-10-09 08:30:36

最近学习了责任链模式的相关文章

设计模式学习-责任链模式

1.定义 避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止. 2.类图 3.代码示例 1 package com.zhaoyangwoo.chainOfResponsibility; 2 3 /** 4 * Created by john on 16/6/16. 5 * 职责链模式,经费审批场景 6 */ 7 public class Responsibility { 8 9 public static void

学习笔记——责任链模式ChainOfResponsibility

责任链模式,主要是通过自己记录一个后继者来判断当前的处理情况.Handler中,再增加一个方法用于设置后继对象,如SetHandler(Handler obj). 然后Handler类以其子类的处理方法Handler()通过判断后继对象是否存在来操作: 1.没有设置后继对象,自己处理事件 2.有后继对象,交给后继者的Handler()处理. 通过这样的不断调用传递,处理责任将到达最终的对象上. 比如,当我们要处理一个网络请求返回时,一般肯定是写一串函数,然后通过判断来依次执行相应操作,如果采用责

编写计算器程序学习JS责任链模式

设计模式中的责任链模式能够很好的处理程序过程的逻辑判断,提高程序可读性. 责任链模式的核心在于责任链上的元素判断能够处理该数据,不能处理的话直接交给它的后继者. 计算器的基本样式: 通过div+css定义计算器的样式,并在每个按钮上绑定事件响应按钮输入. 输入的元素为数字.小数点.加减乘除运算符时,都是直接显示. 输入为清除所有.清除上一次时直接清除. 输入为等号.百分比.开根号.乘方.分之一时,开始计算. 同时在输入框下面显示上次运算的公式. 1.定义责任元素的基类 包括变量next指向他的后

java23种设计模式之十:责任链模式

最近在学习netty中发现其中用到了责任链模式,然后结合自己在写代码中遇到了大量写if...else的情况,决定学习一下责任链模式. 一.什么样的场景下会选择用责任链模式 我们在进行业务逻辑判断时,需要根据传入参数类型的不同做出不同的处理,如果在传入的参数类型相对较少的情况时,可以用if...else来做判断,这样的确是没有什么问题的,但是如果后期由于业务系统的扩展,导致参数类型会随之延伸出很多种不同的处理,这时就需要用责任链模式来抒代码重构,这样会让代码封装的更好.更简洁,阅读性更强,后期如果

PHP设计模式学习笔记: 责任链模式(Chain of Responsibility)

// 抽象书本类 abstract class AbstractBookTopic { abstract function getTopic(); abstract function getTitle(); abstract function setTitle($title_in); } // 书本类,继承自抽象书本类 class BookTopic extends AbstractBookTopic { private $topic; private $title; function __co

责任链模式——HeadFirst设计模式学习笔记

责任链模式:使一个以上的对象都有机会能够处理某个请求 特点: 链中的每个对象包含它下一个对象的引用和对事件的处理方法.请求在这个链上传递,直到链上的某一个对象决定处理此请求 发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,将请求的发送者与接收者解耦 可以动态的改变处理对象成员的顺序,可以动态的增加减少处理对象 用途: 多用于窗口系统中,处理鼠标键盘之类的事件 缺点: 不能保证请求一定执行 不易观察运行时特征,有碍于排错 举例: 不同类型的邮件有不同的处理方式,客户将邮件传给第一个处

设计模式学习笔记(六:责任链模式)

1.1概述 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系.将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止.这就是责任链模式. 责任链模式是使用多个对象处理用户请求的成熟模式,责任链模式的关键是将用户的请求分派给许多对象,这些对象被组织成一个责任链,即每个对象含有后继对象的引用,并要求责任链上的每个对象,如果能处理用户的请求,就做出处理,不再将用户的请求传递给责任链上的下一个对象:如果不能处理用户的请求,就必须将用户的请求传递给责任链上的下一个对象

设计模式学习笔记之责任链模式

责任链模式 使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系.将这些对象连成一条链,并沿着这条链检查该请求,并对其进行处理,或者将它传递给下一个对象. 责任链模式有两个角色组成: 抽象处理者角色:它定义了一个处理请求的接口.当然对于链子的不同实现,也可以在这个角色中实现后继链. 具体处理者角色:实现抽象处理者定义的接口,并处理它所负责的请求. 下面是<设计模式>中给出的适用范围:    1) 有多个的对象可以处理一个请求,哪个对象处理该请求运行时刻自动确定.    2)

设计模式学习笔记之六:责任链模式

我们公司使用的Enovia PLM系统是基于SOA架构的,PLM不仅仅是SAP的主数据源头,同时还需要跟其他的系统(例如供应商的DAM系统及公司的AS400系统)保持交互,系统跟系统的数据交互通过Web Service基于SOAP来实现,具体来说,PLM需要跟如下系统保持交互: 子系统 地区/功能 AFS1 中国,意大利 AFS2 北美 AS400 遗留系统 DAM 供应商 ISR 零售 PLM发送物料主数据到SAP是通过XML文件这种载体的,SAP有个PF(PI)系统专门读取PLM生成在固定共