一文弄懂责任链设计模式

  • 2021-12-12
  • Admin

Reference

[1] zhuanlan.zhihu.com/p/99334096, 本文主要借鉴该篇文章,如有侵权,联系删除

[2] refactoringguru.cn/design-patt…

[3] c.biancheng.net/view/1383.h…

什么是责任链

责任链模式是一种行为设计模式, 允许你将请求沿着处理者链进行发送。 收到请求后, 每个处理者均可对请求进行处理, 或将其传递给链上的下个处理者。

image-20211211232146370

场景

责任链的使用场景还是比较多的

  • 多条件流程判断:权限控制
  • ERP 系统流程审批:总经理、人事经理、项目经理
  • Java 过滤器 的底层实现 Filter

如果不使用该设计模式,那么当需求有所改变时,就会使得代码臃肿或者难以维护,例如下面的例子

反例

假设现在有一个闯关游戏,进入下一关的条件是上一关的分数要高于xx

  1. 游戏一共 3 个关卡
  2. 进入第二关需要第一关的游戏得分大于等于 80
  3. 进入第三关需要第二关的游戏得分大于等于 90

那么代码可以这样写

  1. //第一关
  2. public class FirstPassHandler {
  3. public int handler(){
  4. System.out.println("第一关-->FirstPassHandler");
  5. return 80;
  6. }
  7. }
  8. //第二关
  9. public class SecondPassHandler {
  10. public int handler(){
  11. System.out.println("第二关-->SecondPassHandler");
  12. return 90;
  13. }
  14. }
  15. //第三关
  16. public class ThirdPassHandler {
  17. public int handler(){
  18. System.out.println("第三关-->ThirdPassHandler,这是最后一关啦");
  19. return 95;
  20. }
  21. }
  22. //客户端
  23. public class HandlerClient {
  24. public static void main(String[] args) {
  25. FirstPassHandler firstPassHandler = new FirstPassHandler();//第一关
  26. SecondPassHandler secondPassHandler = new SecondPassHandler();//第二关
  27. ThirdPassHandler thirdPassHandler = new ThirdPassHandler();//第三关
  28. int firstScore = firstPassHandler.handler();
  29. //第一关的分数大于等于80则进入第二关
  30. if(firstScore >= 80){
  31. int secondScore = secondPassHandler.handler();
  32. //第二关的分数大于等于90则进入第二关
  33. if(secondScore >= 90){
  34. thirdPassHandler.handler();
  35. }
  36. }
  37. }
  38. }
  39. 复制代码

那么如果这个游戏有100关,我们的代码很可能就会写成这个样子

  1. if(第1关通过){
  2. // 第2关 游戏
  3. if(第2关通过){
  4. // 第3关 游戏
  5. if(第3关通过){
  6. // 第4关 游戏
  7. if(第4关通过){
  8. // 第5关 游戏
  9. if(第5关通过){
  10. // 第6关 游戏
  11. if(第6关通过){
  12. //...
  13. }
  14. }
  15. }
  16. }
  17. }
  18. }
  19. 复制代码

这种代码不仅冗余,并且当我们要将某两关进行调整时会对代码非常大的改动,这种操作的风险是很高的,因此,该写法非常糟糕

初步改造

如何解决这个问题,我们可以通过链表将每一关连接起来,形成责任链的方式,第一关通过后是第二关,第二关通过后是第三关 ....,这样客户端就不需要进行多重 if 的判断了

  1. public class FirstPassHandler {
  2. /**
  3. * 第一关的下一关是 第二关
  4. */
  5. private SecondPassHandler secondPassHandler;
  6. public void setSecondPassHandler(SecondPassHandler secondPassHandler) {
  7. this.secondPassHandler = secondPassHandler;
  8. }
  9. //本关卡游戏得分
  10. private int play(){
  11. return 80;
  12. }
  13. public int handler(){
  14. System.out.println("第一关-->FirstPassHandler");
  15. if(play() >= 80){
  16. //分数>=80 并且存在下一关才进入下一关
  17. if(this.secondPassHandler != null){
  18. return this.secondPassHandler.handler();
  19. }
  20. }
  21. return 80;
  22. }
  23. }
  24. public class SecondPassHandler {
  25. /**
  26. * 第二关的下一关是 第三关
  27. */
  28. private ThirdPassHandler thirdPassHandler;
  29. public void setThirdPassHandler(ThirdPassHandler thirdPassHandler) {
  30. this.thirdPassHandler = thirdPassHandler;
  31. }
  32. //本关卡游戏得分
  33. private int play(){
  34. return 90;
  35. }
  36. public int handler(){
  37. System.out.println("第二关-->SecondPassHandler");
  38. if(play() >= 90){
  39. //分数>=90 并且存在下一关才进入下一关
  40. if(this.thirdPassHandler != null){
  41. return this.thirdPassHandler.handler();
  42. }
  43. }
  44. return 90;
  45. }
  46. }
  47. public class ThirdPassHandler {
  48. //本关卡游戏得分
  49. private int play(){
  50. return 95;
  51. }
  52. /**
  53. * 这是最后一关,因此没有下一关
  54. */
  55. public int handler(){
  56. System.out.println("第三关-->ThirdPassHandler,这是最后一关啦");
  57. return play();
  58. }
  59. }
  60. public class HandlerClient {
  61. public static void main(String[] args) {
  62. FirstPassHandler firstPassHandler = new FirstPassHandler();//第一关
  63. SecondPassHandler secondPassHandler = new SecondPassHandler();//第二关
  64. ThirdPassHandler thirdPassHandler = new ThirdPassHandler();//第三关
  65. firstPassHandler.setSecondPassHandler(secondPassHandler);//第一关的下一关是第二关
  66. secondPassHandler.setThirdPassHandler(thirdPassHandler);//第二关的下一关是第三关
  67. //说明:因为第三关是最后一关,因此没有下一关
  68. //开始调用第一关 每一个关卡是否进入下一关卡 在每个关卡中判断
  69. firstPassHandler.handler();
  70. }
  71. }
  72. 复制代码

缺点

现有模式的缺点

  • 每个关卡中都有下一关的成员变量并且是不一样的,形成链很不方便
  • 代码的扩展性非常不好

责任链改造

  • 既然每个关卡中都有下一关的成员变量并且是不一样的,那么我们可以在关卡上抽象出一个父类或者接口,然后每个具体的关卡去继承或者实现

有了思路,我们先来简单介绍一下责任链设计模式的基本组成

  • 抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
  • 具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
  • 客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。

image-20211211233740916

  1. public abstract class AbstractHandler {
  2. /**
  3. * 下一关用当前抽象类来接收
  4. */
  5. protected AbstractHandler next;
  6. public void setNext(AbstractHandler next) {
  7. this.next = next;
  8. }
  9. public abstract int handler();
  10. }
  11. public class FirstPassHandler extends AbstractHandler{
  12. private int play(){
  13. return 80;
  14. }
  15. @Override
  16. public int handler(){
  17. System.out.println("第一关-->FirstPassHandler");
  18. int score = play();
  19. if(score >= 80){
  20. //分数>=80 并且存在下一关才进入下一关
  21. if(this.next != null){
  22. return this.next.handler();
  23. }
  24. }
  25. return score;
  26. }
  27. }
  28. public class SecondPassHandler extends AbstractHandler{
  29. private int play(){
  30. return 90;
  31. }
  32. public int handler(){
  33. System.out.println("第二关-->SecondPassHandler");
  34. int score = play();
  35. if(score >= 90){
  36. //分数>=90 并且存在下一关才进入下一关
  37. if(this.next != null){
  38. return this.next.handler();
  39. }
  40. }
  41. return score;
  42. }
  43. }
  44. public class ThirdPassHandler extends AbstractHandler{
  45. private int play(){
  46. return 95;
  47. }
  48. public int handler(){
  49. System.out.println("第三关-->ThirdPassHandler");
  50. int score = play();
  51. if(score >= 95){
  52. //分数>=95 并且存在下一关才进入下一关
  53. if(this.next != null){
  54. return this.next.handler();
  55. }
  56. }
  57. return score;
  58. }
  59. }
  60. public class HandlerClient {
  61. public static void main(String[] args) {
  62. FirstPassHandler firstPassHandler = new FirstPassHandler();//第一关
  63. SecondPassHandler secondPassHandler = new SecondPassHandler();//第二关
  64. ThirdPassHandler thirdPassHandler = new ThirdPassHandler();//第三关
  65. // 和上面没有更改的客户端代码相比,只有这里的set方法发生变化,其他都是一样的
  66. firstPassHandler.setNext(secondPassHandler);//第一关的下一关是第二关
  67. secondPassHandler.setNext(thirdPassHandler);//第二关的下一关是第三关
  68. //说明:因为第三关是最后一关,因此没有下一关
  69. //从第一个关卡开始
  70. firstPassHandler.handler();
  71. }

联系站长

QQ:769220720

Copyright © SibooSoft All right reserved 津ICP备19011444号