0x1、定义
原始定义
将请求的发送和接收解耦,让多个接收对象都有机会处理这个请求。将这些接收对象串成一条链,并沿着这条链传递这个请求,知道链上某个接收对象能够处理它为止。
定义看起来有点抽象,简单点就是:构建一个处理流水线来对一次请求进行多次处理。
还是不懂?没关系,写个简单例子帮助理解~
0x2、写个简单例子
你问哥哥、粑粑、麻麻拿钱,门槛依次是100,500,1000,只能按照一层层往下走,比如:
100块以下的开销,你可以找哥哥解决,100以上500以下的你得找爸爸,500以上1000以下你得找麻麻~
直接if-else一把梭,不难写出这样的代码:
public class ChainTest { public static void main(String[] args) { Random random = new Random(); int needMoney = random.nextInt(1500); System.out.println("需要:" + needMoney + "块~"); if(needMoney < 100) { System.out.println("哥哥:小于100块哥哥还是有的,给你~"); } else { System.out.println("哥哥:大于100块哥哥木有那么多钱,找粑粑去吧~"); if(needMoney < 500) { System.out.println("粑粑:500块以内,粑粑有,给你~"); } else { System.out.println("粑粑:大于500,粑粑木有,找妈妈去吧~"); if(needMoney < 1000) { System.out.println("麻麻:1000以内,麻麻可以报销,给你~"); } else { System.out.println("麻麻:你要那么多钱干嘛?"); } } } } }
代码运行输出结果如下:
如果再来个疼爱孙子的爷爷,超过1000的可以找他报销,又得嵌套一个if-else,在一些复杂的实际业务场景,这样的写法可能要套十几层,对于这种流水线式加工的场景,其实可以使用责任链模式解耦~
把伸手要钱这个行为看成一个 请求,你是 请求者,家人是 接收者,他们会按照特定的顺序:哥哥 → 粑粑 → 麻麻 → 爷爷 对你的请求进行处理,在自己额度范围内,就不往下走,不在就往下走,直到最后一个接收者为止。这个特定顺序可以看成一条 链,请求就是沿着这样的链往后传递~
一种最简单的实现方式,就是:每个接收者持有后继接收者实例,递归调用直到没有后继接收者为止,实现代码如下:
// 抽象处理者 public abstract class AbstractHandler { // 下一个处理者 private AbstractHandler nextHandler; public AbstractHandler getNextHandler() { return nextHandler; } public void setNextHandler(AbstractHandler nextHandler) { this.nextHandler = nextHandler; } // 请求处理 public abstract void handleRequest(String msg, int money); } // 具体处理者 public class BrotherHandler extends AbstractHandler { @Override public void handleRequest(String msg, int money) { System.out.println(msg + ":" + money + "块~"); if(money < 100) { System.out.println("哥哥:小于100块哥哥还是有的,给你~"); } else { System.out.println("哥哥:大于100块哥哥木有那么多钱,找粑粑去吧~"); if(getNextHandler() != null) getNextHandler().handleRequest(msg, money); } } } public class FatherHandler extends AbstractHandler { @Override public void handleRequest(String msg, int money) { if(money < 500) { System.out.println("粑粑:500块以内,粑粑有,给你~"); } else { System.out.println("粑粑:大于500,粑粑木有,找妈妈去吧~"); if(getNextHandler() != null) getNextHandler().handleRequest(msg, money); } } } public class MotherHandler extends AbstractHandler { @Override public void handleRequest(String msg, int money) { if(money < 1000) { System.out.println("麻麻:1000以内,麻麻可以报销,给你~"); } else { System.out.println("麻麻:你要那么多钱干嘛?"); if(getNextHandler() != null) getNextHandler().handleRequest(msg, money); } } } // 测试用例 public class ChainTest { public static void main(String[] args) { Random random = new Random(); int needMoney = random.nextInt(1500); BrotherHandler brotherHandler = new BrotherHandler(); FatherHandler fatherHandler = new FatherHandler(); MotherHandler motherHandler = new MotherHandler(); // 指定下一个接收者 brotherHandler.setNextHandler(fatherHandler); fatherHandler.setNextHandler(motherHandler); // 开始请求传递 brotherHandler.handleRequest("狗子要钱", needMoney); } }
代码运行结果输出如下:
责任链模式解耦后,此时我们要再添加一个爷爷就很简单了,四步:
继承AbstractHandler → 重写handleRequest() → 初始化爷爷实例 → motherHandler.setNextHandler()
如果要再来个曾祖父或高曾祖父,也是这样操作,你可能会吐槽是不是有点过度设计,简单的if-else嵌套就能解决,这样会写多很多类,有点复杂化了。
应用设计模式主要是为了:应付代码复杂性,让其满足开闭原则,提高代码扩展性。而将大块代码逻辑拆解成函数、大类拆分成小类,是应对代码复杂性的常用方法。
此处使用责任链模式,把处理请求的函数拆分出来,设计成独立的类,简化了ChainTest类,使其不至于代码过多,过复杂。
客户端代码新增一个接收者,不需要去修改框架的代码,只需基于框架提供的扩展点进行扩展,可以说是:在框架代码范围内实现了开闭原则。
老规矩,带出UML类图、组成角色、使用场景及优缺点的总结
- Handler (抽象处理者) → 定义了处理请求的接口或抽象类,提供处理请求的方法和设置下一个处理者的方法;
- ConcreteHandler (具体处理者) → 抽象处理者的具体实现,按照链条顺序对请求进行具体处理;
使用场景
- 运行时需要动态使用多个关联对象来处理同一次请求,如编译打包发布流程;
- 不想让使用者知道具体的处理逻辑,如权限校验的登录拦截器;
- 需动态替换流程处理中的流程对象;
- if-else多层嵌套或冗长的switch结构解耦;
优点
- 降低客户端(请求者)与处理链条上对象(接收者)间的耦合度;
- 动态组合,简化了对象前后关联处理的复杂性,只需存储一个指向后继者的引用;
- 扩展灵活,新增具体请求者时无需修改原有系统的代码,满足开闭原则;
缺点
- 产生许多细颗粒对象;
- 比较长的责任链,可能涉及多个处理对象,可能存在性能问题,及调试不方便;
- 建链不当,可能造成循环调用,导致死循环,进而导致堆栈溢出错误;
另外,除了使用上述 递归的方式形成链条 实现责任链模式外,还可以使用数据结构(数组、列表、链表等) 按顺序保存具体处理者实例,然后遍历的方式实现~