责任链模式
高铁线路
在现实生活中,一个事件需要经过多个对象处理是很常见的场景。例如,采购审批流程、请假流程等。公司员工请假,可批假的领导有部门负责人、副总经理、总经理等,但每个领导能批准的天数不同,员工必须根据需要请假的天数去找不同的领导签名,也就是说员工必须记住每个领导的姓名、电话和地址等信息,这无疑增加了难度。
那么有没有一种模式,只需要知道出发点,中间经过多少环节并不用用户关心,顺着流程就可以达到最终的结果。就好比搭高铁,只需要知道起点和终点即可,买票起点搭车到站下车,中间经过多少站点,每个站点停留了多久时间不用乘客关心,甚至可以睡觉。
![查看源图像](https://pic.zsucai.com/files/2020/1124/ffpic0515ewsfr04039.jpg)
高铁线路上的站点
要使用户无感中间站点,高铁线路工作者需要把每个站点预设好,并且把它串起来起来,让高铁顺滑跑下去。那么如何串起来呢,需要每个站记住下一个站就好,这样就可以索引着跑下去。
尝试使用一个抽象类来代表站台吧。
package com.skystep.设计模式.责任链模式;
public abstract class Station {
// 标记下一个站
protected Station next;
public void setNext(Station next) {
this.next = next;
}
public Station getNext() {
return next;
}
// 模拟站点停留
public abstract void stay();
}
OK,先建立一个叫做 “深圳北站” 的站点,作为起点站。
package com.skystep.设计模式.责任链模式;
import java.util.Objects;
public class ShenZhenNorthStation extends Station{
@Override
public void stay() {
System.out.println("10s后列车从深圳北站出发..。");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (Objects.nonNull(next)) next.stay();
}
}
OK,再建立一个叫做 “东莞虎门站” 的站点,作为中间站。
package com.skystep.设计模式.责任链模式;
import java.util.Objects;
public class DongGuanHuMenStation extends Station {
@Override
public void stay() {
System.out.println("东莞虎门站到了,请下车的乘客下车..。");
System.out.println("此站停留20s");
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (Objects.nonNull(next)) next.stay();
}
}
OK,再建立一个叫做 “广州南站” 的站点,作为终点站。
package com.skystep.设计模式.责任链模式;
import java.util.Objects;
public class GuangzhouSouthStation extends Station {
@Override
public void stay() {
System.out.println("广州南站到了,该站是终点站,感谢搭乘.。");
System.out.println("此站停留20s");
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (Objects.nonNull(next)) next.stay();
}
}
名叫深广线的线路
OK,是时候连接起一条高铁路线了,就叫“深广线”线吧。
package com.skystep.设计模式.责任链模式;
public class ShenZhen2GuangZhou {
private final Station station;
// 把站点一个个连起来
public ShenZhen2GuangZhou() {
ShenZhenNorthStation shenZhenNorthStation = new ShenZhenNorthStation();
DongGuanHuMenStation dongGuanHuMenStation = new DongGuanHuMenStation();
GuangzhouSouthStation guangzhouSouthStation = new GuangzhouSouthStation();
shenZhenNorthStation.setNext(dongGuanHuMenStation);
dongGuanHuMenStation.setNext(guangzhouSouthStation);
station = shenZhenNorthStation;
}
// 跑起来
public void run() {
station.stay();
}
}
那么写个客户端类,跑起来吧。
package com.skystep.设计模式.责任链模式;
public class Test {
public static void main(String[] args) {
ShenZhen2GuangZhou shenZhen2GuangZhou = new ShenZhen2GuangZhou();
shenZhen2GuangZhou.run();
}
}
高铁从深圳北站出发,途径东莞虎门站,最终到广州南站。
10s后列车从深圳北站出发..。
东莞虎门站到了,请下车的乘客下车..。
此站停留20s
广州南站到了,该站是终点站,感谢搭乘.。
此站停留20s
有了 ShenZhen2GuangZhou 这条链路,使用者将不再关系其内部实现了,不关心经过多少站点。反正可以从深圳到广州。对于高铁线路工作者来说,可以在这中间添加其他的站点,不影响每个站点原先的业务。此外每个站点还可以组合形成其他的线路。
![查看源图像](https://tse1-mm.cn.bing.net/th/id/R-C.85e87841ebe32b9c4e18b4a688333494?rik=XO97zpL4BCNnhw&riu=http%3a%2f%2fpic.downyi.com%2fupload%2f2018-12%2f20181214152610108100.jpg&ehk=lOeh3IByChcx%2bxQEtGU2TJtRLDijrLO15bord2AcNDA%3d&risl=&pid=ImgRaw&r=0&sres=1&sresct=1)
回归模式
这种把处理环节抽象串联起来的模式叫做 “责任链模式”,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,因此职责链将请求的发送者和请求的处理者解耦了。换成人话,要从深圳到广州,使用 ShenZhen2GuangZhou 类就可以了,ShenZhen2GuangZhou 中已经做好每个站点的处理。“责任链模式” 是一种行为模式,常常被用作消息的的多道过滤处理。
责任链模式中,存在着两个重要角色:
- Handler: 抽象处理者 相当于站台 Station ;
- concreteHandler:具体的处理者是处理请求的具体角色,相当于 ShenZhenNorthStation、DongGuanHuMenStation。
责任链模式的类图如下:
应用场景
责任链在实际业务中有很多应用,
- 有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定;
- 在不明确指定接收者的情况下,向多个对象中的一个提交一个请求;
- 可动态指定一组对象处理请求;
深广线的线路优化
优化重复代码
在具体的处理者每个类都存在以下代码:
if (Objects.nonNull(next)) next.stay();
于是配合模板设计模式,把这个代码抽到抽象类中,
package com.skystep.设计模式.责任链模式;
import java.util.Objects;
public abstract class Station {
protected Station next;
public void setNext(Station next) {
this.next = next;
}
public Station getNext() {
return next;
}
// 子类必须重写
protected void _stay() {
throw new UnsupportedOperationException();
}
public void stay() {
_stay();
if (Objects.nonNull(next)) next._stay();
}
}
对具体的处理者进行修改,
package com.skystep.设计模式.责任链模式;
import java.util.Objects;
public class DongGuanHuMenStation extends Station {
@Override
public void _stay() {
System.out.println("东莞虎门站到了,请下车的乘客下车..。");
System.out.println("此站停留20s");
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
将返回值作为参数
如果需要将上次节点执行的结果作为下一个节点的参数,那么抽象处理类可以做以下调整。
package com.skystep.设计模式.责任链模式;
import java.util.Objects;
public abstract class Station {
protected Station next;
public void setNext(Station next) {
this.next = next;
}
public Station getNext() {
return next;
}
// 子类必须重写
protected Object _stay(Object param) {
throw new UnsupportedOperationException();
}
public void stay(Object param) {
Object object = _stay(param);
if (Objects.nonNull(next)) next._stay(object);
}
}