责任链模式
老李的苦恼
每个人在出生的时候,都早已在暗中被标好了三六九等。
老李是一名建筑工地的木匠,和大多数生活在社会最底层的农民工一样,一辈子老实本分,胆小怕事。在他们的心中,谁当老爷都没有区别,世界发展如何也与他们无关,只要包工头能按时发工资,只要小家平安无事就够了,平时受点欺负,累点苦点也没办法,能忍则忍了。
并不是你安分守己,世界就会给你公平公正,社会它牢记着你的出身。
老李最近遇到了一件糟心事,起因是在工地干活的时候,被钢管砸断了鼻梁骨和颧骨,流了一个多小时血,才送到医院,包工头交了医疗费后就不再过问,老李在医院做手术加恢复总共就住了一个星期,还在流鼻血就匆匆出院,理由是怕给包工头添麻烦。
出院后一直头昏流鼻血也自己扛着,期间包工头不管不问老李身体无法支撑他出门,态度恶劣甚至辱骂让他去送材料到公司让公司报销,不送去就再也不管他。当老李问及赔偿事宜怎么处理时,包工头云淡风轻的说人现在又没事,给个万把块不就好了,老李一辈子太胆小懦弱了,不会又不敢维权,怕包工头前年和去年拖欠的工资不给自己(包工头没打欠条),自己一个人在家生闷气,好几天不吃饭。
会哭的孩子有糖吃,听话的孩子去奔涌吧。
老李就是这样的一个人,一辈子默默的吃着闷亏,遇事只知道生闷气,惩罚自己然后劝说自己妥协。这样的心态绝不止老李一个人,自古以来我们的老百姓就是这样,因为出身贱民,因为怕给任何人带去麻烦,因为经不起磨难,因为这样的人最好管理,因为你的出身就是你的原罪。
因为我曾经看见过光,所以我再也不怕黑暗。
我就是想在每篇文章的开头写一些人和事,想表达一些可以值得思考的东西。老李的家人曾经去过项目部找公司,也找过包工头,但是都没有人处理。不管哪个社会,没人没钱都不好办事,现实且残忍,如果没有关系,老李要么继续熟练的忍气吞声,要么自己一直找公司或者包工头纠缠无果。
问题分析
- 社会现实角度:老李吃了那么大的亏,现在公司和包工头都不管不问,相互踢皮球,而老李又软弱惯了,无可奈何。要怎么解决这个问题呢?老李有个亲戚老赵听到此事非常气愤,老实人就要任人宰割吗?老赵利用自己的人脉关系直接找到了公司的大老板,大老板说这事包在他身上,他让项目经理去处理,不用老李在来回跑了,等处理结果就好了。
- 我们再从技术角度分析一下,一是老李不知道具体找谁处理工伤事故,也就是找不到处理他赔偿请求的责任人,另一个就是老李需要不断的和公司或者包工头纠缠,也就是耦合性非常高,而这两点都违背了我们日常开发的设计原则。
这个时候,我们本文的重点,责任链模式就正式登场了。
责任链模式
什么是责任链模式?
责任链模式(Chain of Responsibility):使多个对象都有机会去处理请求。从而避免请求的发送者和接收者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
责任链模式也叫职责链模式,属于行为型模式。通过责任链模式,可以为某个请求创建一个对象链。每个对象按照顺序检查该请求,并处理该请求,或者传给链中的下一个对象处理。
责任链模式的三个角色
责任链模式主要有以下三个角色:
- 抽象处理者角色(Handler):定义一个处理请求的接口,内部包含一个后继处理者的引用和一个处理请求的方法,抽象处理者可以是一个接口或者抽象类。
- 具体处理者角色(ConcreteHandler):实现抽象处理者。如果能够处理请求则处理,否则就将该请求转发给它的后继处理者。在具体处理者中可以访问链中下一个对象,以便请求的转发。
- 请求发送者角色:创建处理链,同时向链头的具体处理者对象提交请求,它并不关心处理细节和请求的传递过程。
责任链UML图
代码实例
上文中我们知道老李的亲戚老赵出面才摆平了此事,那老赵是怎么解决的呢?老赵找到公司的大老板,大老板让项目经理处理,项目经理让包工头或者其他人配合处理,这就形成了一个对象责任链,总有一个对象处理赔偿的请求,而老李再也不用关心具体是谁处理的工伤赔偿的请求。
下面我们用代码来演示一遍。
1、编写抽象处理者角色Handler
package com.mazhichu.designpatterns.responsibility;
import java.math.BigDecimal;
/**
* @ClassName: DealWorkInjuryRequestHandler
* @Description: 抽象处理者角色
* @Author: Moore
* @Date: 2020-06-29 9:39
* @Version: V1.0
*/
public abstract class DealWorkInjuryRequestHandler {
/**
* 如果处理不了,设置后继处理者
*/
protected DealWorkInjuryRequestHandler successor;
/**
* 处理请求的方法
* @param compensationPayments
*/
public abstract void dealWorkInjury(BigDecimal compensationPayments);
public void setSuccessor(DealWorkInjuryRequestHandler successor) {
this.successor = successor;
}
}
2、编写具体处理者角色老李的亲戚老赵。
package com.mazhichu.designpatterns.responsibility;
import java.math.BigDecimal;
/**
* @ClassName: LaoZhaoHandler
* @Description: 具体处理者角色:老赵
* @Author: Moore
* @Date: 2020-06-29 9:40
* @Version: V1.0
*/
public class LaoZhaoHandler extends DealWorkInjuryRequestHandler{
@Override
public void dealWorkInjury(BigDecimal compensationPayments) {
System.out.println("老赵:老李别担心,赔偿款一共"+compensationPayments+"元,我已经找了他们大老板去处理了。");
successor.dealWorkInjury(compensationPayments);
}
}
3、编写具体处理者角色承建公司大老板。
package com.mazhichu.designpatterns.responsibility;
import java.math.BigDecimal;
/**
* @ClassName: LaoZhaoHandler
* @Description: 具体处理者角色:承建公司大老板
* @Author: Moore
* @Date: 2020-06-29 9:40
* @Version: V1.0
*/
public class BossHandler extends DealWorkInjuryRequestHandler{
@Override
public void dealWorkInjury(BigDecimal compensationPayments) {
if(compensationPayments.compareTo(BigDecimal.valueOf(100000))>0){
System.out.println("大老板"+compensationPayments+"赔偿款,先拖拖,找人也不行。");
}else {
System.out.println("大老板"+compensationPayments+"赔偿款,才这点钱,让项目经理去处理。");
successor.dealWorkInjury(compensationPayments);
}
}
}
4、编写具体处理者角色包工头
package com.mazhichu.designpatterns.responsibility;
import java.math.BigDecimal;
import java.math.MathContext;
/**
* @ClassName: LaoZhaoHandler
* @Description: 具体处理者角色:承建公司包工头
* @Author: Moore
* @Date: 2020-06-29 9:40
* @Version: V1.0
*/
public class ContractorHandler extends DealWorkInjuryRequestHandler{
@Override
public void dealWorkInjury(BigDecimal compensationPayments) {
if(compensationPayments.compareTo(BigDecimal.valueOf(30000))>0){
System.out.println("包工头:上面压我,倒霉玩意,"+compensationPayments+"赔偿款,我要赔"+compensationPayments.multiply(BigDecimal.valueOf(0.3), MathContext.DECIMAL32));
}else {
System.out.println("包工头:上面压我,倒霉玩意,我要自己掏"+compensationPayments+"赔偿款。");
}
}
}
5、我们看看老李的维权之路是怎样的,编写客户端请求发送者角色老李。
package com.mazhichu.designpatterns.responsibility;
import java.math.BigDecimal;
/**
* @ClassName: Laoli
* @Description: 请求发送者角色:老李
* @Author: Moore
* @Date: 2020-06-29 10:44
* @Version: V1.0
*/
public class Laoli {
public static void main(String[] args) {
DealWorkInjuryRequestHandler lzHandler = new LaoZhaoHandler();
DealWorkInjuryRequestHandler bossHandler = new BossHandler();
DealWorkInjuryRequestHandler manageHandler = new ManageHandler();
DealWorkInjuryRequestHandler contractorHandler = new ContractorHandler();
lzHandler.setSuccessor(bossHandler);
bossHandler.setSuccessor(manageHandler);
manageHandler.setSuccessor(contractorHandler);
lzHandler.dealWorkInjury(BigDecimal.valueOf(120000));
System.out.println("-------------------------------------------------\n");
lzHandler.dealWorkInjury(BigDecimal.valueOf(80000));
System.out.println("-------------------------------------------------\n");
lzHandler.dealWorkInjury(BigDecimal.valueOf(20000));
}
}
可以看出,在代码实例中我们创建了四个处理者对象,并指定第一个处理者对象的下家是第二个处理者对象,一直到第四个处理对象包工头为止。然后客户端老李将请求传递给第一个处理者对象老赵,老李不用再关心具体是谁处理了工伤赔偿。他只知道,如果没有老赵,他最终可能只能认命,听话不闹事就能拿到工资就好。
老李是太胆小又怕事,其实他完全可以拿着伤残鉴定去劳动局监察大队去投诉,这样就会在责任链上多一个具体处理者角色,监察大队会继续追查下去,找到链上该负责的处理对象,环环相扣,最终给老李一个交代。
总结
熟悉数据结构的可以想象一下链表的结构,每一个节点都有一个指向后继节点的引用。责任链模式在我们的日常生活中也很常见,比如常见的报销或者请假流程,客户端只需要将请求提交到链上,就不用关心是哪个具体处理对象处理了请求。也可以动态的增加链上的具体处理角色,只要指定后继者处理对象就好。
适用场景
- 一个请求的处理需要多个对象当中的一个或者几个协作处理,具体哪个对象处理该请求由运行时刻自动确定。
- 在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
- 可动态指定一组对象处理请求。
责任链模式优点
- 将请求的发送者和接收者解耦。
- 简化了对象。使得对象不需要知道链的结构。
- 通过改变责任链内的对象或者调动责任链的顺序,允许动态增加或者删除责任链对象。
- 在系统中增加一个新的具体请求处理者时无须修改原有系统的代码,只需要在客户端重新建链即可,从这一点来看是符合“开闭原则”的。
责任链模式缺点
- 责任链在处理请求时,可能会耗时过长。
- 责任链较长时,对象可能会涉及多个,从而影响系统运行性能,消耗内存。
- 并不保证请求一定会被执行,如果指定后继处理者不当,可能会造成死循环。
纯的与不纯的责任链模式
一个纯的责任链模式要求一个具体的处理者对象只能在两个行为中选择一个:一是承担责任,而是把责任推给下家。不允许出现某一个具体处理者对象在承担了一部分责任后又把责任向下传的情况。
在一个纯的责任链模式里面,一个请求必须被某一个处理者对象所接收;在一个不纯的责任链模式里面,一个请求可以最终不被任何接收端对象所接收。
纯的责任链模式的实际例子很难找到,一般看到的例子均是不纯的责任链模式的实现。
后话
责任链模式就讲解到这,其实很简单很好理解。难以理解的是其实老李到现在还没有处理好他的工伤赔偿事宜,不知道何时,农民工才能真正受到这个社会的尊重,不知道何时,我们才能骄傲的说我们是一个法治社会。让所有人有法可依,有理可证,有平等和公平可言。
如果可以更好,请给我光明。
如果不能更好,希望别太坏。
小惊喜
认识我的或者关注【码之初】有一段时间的朋友都知道,我是个与世无争的完全自己打理的小号主,能力一般,技术有限,坚持更新,只因热爱。没有精力和财力推广,所以也不曾盈利过,但因为同样的热爱,我们在此相遇,为了感恩这份相遇,我决定,从2020年7月1日开始,每半个月查一下后台数据,阅读最多的前三名,每人一个茶叶蛋,分享文章的前三名,依次为红牛、脉动、农夫山泉,每个月1号、16号公布结果并折现发给这些陪伴码之初的朋友,经济萧条,金额较小,承蒙不弃,皆为心意。