0%

设计模式之行为型模式(一):责任链模式

1. 责任链模式概述

  • 概念
    • 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象链成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止
    • 责任链模式主要用于处理职责相同,程度不同的类

2. 责任链模式 Demo

责任链模式

  1. “解决 bug 程序” 1.0:让三种类型的程序员依次尝试解决 bug,如果 bug 难度在自己能解决的范围之内,则自己处理

    • 新建一个 bug 类

      1
      2
      3
      4
      5
      6
      7
      8
      public class Bug {
      // bug 的难度值
      int value;

      public Bug(int value) {
      this.value = value;
      }
      }
    • 新建一个程序员类

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      public class Programmer {
      // 程序员类型:菜鸟、普通、优秀
      public String type;

      public Programmer(String type) {
      this.type = type;
      }

      public void solve(Bug bug) {
      System.out.println(type + "程序员解决了一个难度为 " + bug.value + " 的 bug");
      }
      }
    • 客户端

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      import org.junit.Test;

      public class Client {
      @Test
      public void test() {
      Programmer newbie = new Programmer("菜鸟");
      Programmer normal = new Programmer("普通");
      Programmer good = new Programmer("优秀");

      Bug easy = new Bug(20);
      Bug middle = new Bug(50);
      Bug hard = new Bug(100);

      // 依次尝试解决 bug
      handleBug(newbie, easy);
      handleBug(normal, easy);
      handleBug(good, easy);

      handleBug(newbie, middle);
      handleBug(normal, middle);
      handleBug(good, middle);

      handleBug(newbie, hard);
      handleBug(normal, hard);
      handleBug(good, hard);
      }

      public void handleBug(Programmer programmer, Bug bug) {
      if (programmer.type.equals("菜鸟") && bug.value > 0 && bug.value <= 20) {
      programmer.solve(bug);
      } else if (programmer.type.equals("普通") && bug.value > 20 && bug.value <= 50) {
      programmer.solve(bug);
      } else if (programmer.type.equals("优秀") && bug.value > 50 && bug.value <= 100) {
      programmer.solve(bug);
      }
      }
      }
    • 运行程序,输出如下

      1
      2
      3
      菜鸟程序员解决了一个难度为 20 的 bug
      普通程序员解决了一个难度为 50 的 bug
      优秀程序员解决了一个难度为 100 的 bug
    • 分析:在这个程序中,让每个程序员都尝试处理了每一个 bug,也就相当于大家围着讨论每个 bug 该由谁解决,这无疑是非常低效的做法

  2. “解决 bug 程序” 2.0:实际上,许多公司会选择让项目经理来分派任务,项目经理会根据 bug 的难度指派给不同的人解决

    • 引入项目经理类

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      public class ProjectManager {
      Programmer newbie = new Programmer("菜鸟");
      Programmer normal = new Programmer("普通");
      Programmer good = new Programmer("优秀");

      public void assignBug(Bug bug) {
      if (bug.value > 0 && bug.value <= 20) {
      System.out.println("项目经理将这个简单的 bug 分配给了菜鸟程序员");
      newbie.solve(bug);
      } else if (bug.value > 20 && bug.value <= 50) {
      System.out.println("项目经理将这个中等的 bug 分配给了普通程序员");
      normal.solve(bug);
      } else if (bug.value > 50 && bug.value <= 100) {
      System.out.println("项目经理将这个困难的 bug 分配给了优秀程序员");
      good.solve(bug);
      }
      }
      }
    • 修改客户端

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      import org.junit.Test;

      public class Client2 {
      @Test
      public void test() {
      ProjectManager manager = new ProjectManager();

      Bug easy = new Bug(20);
      Bug middle = new Bug(50);
      Bug hard = new Bug(100);

      manager.assignBug(easy);
      manager.assignBug(middle);
      manager.assignBug(hard);
      }
      }
    • 运行程序,输出如下

      1
      2
      3
      4
      5
      6
      项目经理将这个简单的 bug 分配给了菜鸟程序员
      菜鸟程序员解决了一个难度为 20bug
      项目经理将这个中等的 bug 分配给了普通程序员
      普通程序员解决了一个难度为 50bug
      项目经理将这个困难的 bug 分配给了优秀程序员
      优秀程序员解决了一个难度为 100bug
    • 分析

      • 在这个经过修改的程序中,醒目经理一个人承担了分配所有 bug 这个体力活,程序没有变得简洁,只是把复杂的逻辑从客户端转移到了项目经理类中
      • 项目经理承担了过多的职责,如果以后新增一类程序员,必须改动项目经理类,将其处理 bug 的职责插入分支判断语句中,违反了单一职责原则和开闭原则
  3. “解决 bug 程序” 3.0责任链模式

    • 修改客户端

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      import org.junit.Test;

      public class Client3 {
      @Test
      public void test() throws Exception {
      Programmer newbie = new Programmer("菜鸟");
      Programmer normal = new Programmer("普通");
      Programmer good = new Programmer("优秀");

      Bug easy = new Bug(20);
      Bug middle = new Bug(50);
      Bug hard = new Bug(100);

      // 链式传递责任
      if (!handleBug(newbie, easy)) {
      if (!handleBug(normal, easy)) {
      if (!handleBug(good, easy)) {
      throw new Exception("Kill the fake good programmer!");
      }
      }
      }

      if (!handleBug(newbie, middle)) {
      if (!handleBug(normal, middle)) {
      if (!handleBug(good, middle)) {
      throw new Exception("Kill the fake good programmer!");
      }
      }
      }

      if (!handleBug(newbie, hard)) {
      if (!handleBug(normal, hard)) {
      if (!handleBug(good, hard)) {
      throw new Exception("Kill the fake good programmer!");
      }
      }
      }
      }

      public boolean handleBug(Programmer programmer, Bug bug) {
      if (programmer.type.equals("菜鸟") && bug.value > 0 && bug.value <= 20) {
      programmer.solve(bug);
      return true;
      } else if (programmer.type.equals("普通") && bug.value > 20 && bug.value <= 50) {
      programmer.solve(bug);
      return true;
      } else if (programmer.type.equals("优秀") && bug.value > 50 && bug.value <= 100) {
      programmer.solve(bug);
      return true;
      }
      return false;
      }
      }
    • 运行程序,输出如下

      1
      2
      3
      菜鸟程序员解决了一个难度为 20 的 bug
      普通程序员解决了一个难度为 50 的 bug
      优秀程序员解决了一个难度为 100 的 bug
    • 分析

      • 三个嵌套的 if 条件语句组成了一条菜鸟->普通->优秀的责任链。使 handleBug() 方法返回一个 boolean 值,如果此 bug 被处理了返回 true,否则返回 false,使得责任沿着这条链继续传递
      • 本例中这个责任链和平时工作中使用的不太一样。事实上,这段代码已经很好地体现了责任链模式的基本思想。实际中使用的责任链模式只是在面向对象的基础上,将这段代码封装了一下
  4. “解决 bug 程序” 4.0更规范的封装后的责任链模式

    • 新建一个程序员抽象类

      1
      2
      3
      4
      5
      6
      7
      8
      9
      public abstract class Programmer {
      protected Programmer next;

      public void setNext(Programmer next) {
      this.next = next;
      }

      abstract void handle(Bug bug);
      }
    • 新建菜鸟程序员类

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      public class NewbieProgrammer extends Programmer {

      @Override
      public void handle(Bug bug) {
      if (bug.value > 0 && bug.value <= 20) {
      solve(bug);
      } else if (next != null) {
      next.handle(bug);
      }
      }

      private void solve(Bug bug) {
      System.out.println("菜鸟程序员解决了一个难度为 " + bug.value + " 的 bug");
      }
      }
    • 新建普通程序员类

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      public class NormalProgrammer extends Programmer {

      @Override
      public void handle(Bug bug) {
      if (bug.value > 20 && bug.value <= 50) {
      solve(bug);
      } else if (next != null) {
      next.handle(bug);
      }
      }

      private void solve(Bug bug) {
      System.out.println("普通程序员解决了一个难度为 " + bug.value + " 的 bug");
      }
      }
    • 新建优秀程序员类

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      public class GoodProgrammer extends Programmer {

      @Override
      public void handle(Bug bug) {
      if (bug.value > 50 && bug.value <= 100) {
      solve(bug);
      } else if (next != null) {
      next.handle(bug);
      }
      }

      private void solve(Bug bug) {
      System.out.println("优秀程序员解决了一个难度为 " + bug.value + " 的 bug");
      }
      }
    • 客户端测试

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      import org.junit.Test;

      public class Client4 {
      @Test
      public void test() {
      NewbieProgrammer newbie = new NewbieProgrammer();
      NormalProgrammer normal = new NormalProgrammer();
      GoodProgrammer good = new GoodProgrammer();

      Bug easy = new Bug(20);
      Bug middle = new Bug(50);
      Bug hard = new Bug(100);

      // 组成责任链
      newbie.setNext(normal);
      normal.setNext(good);

      // 从菜鸟程序员开始,沿着责任链传递
      newbie.handle(easy);
      newbie.handle(middle);
      newbie.handle(hard);
      }
      }
    • 分析

      • 通过 setNext() 封装方法将三个程序员组成了一条责任链,由菜鸟程序员接收所有的 bug,发现自己不能处理的 bug,就传递给普通程序员,普通程序员收到 bug 后,如果发现自己不能解决,则传递给优秀程序员,这就是规范的责任链模式的写法了
      • 责任链思想在生活中有很多应用,比如假期审批、加薪申请、处理客户投诉等

3. 责任链模式的优缺点

  • 优点

    • 降低了对象之间的耦合度。在责任链模式中,客户只需要将请求发送到责任链上即可,无须关心请求的处理细节和请求的传递过程,所以责任链将请求的发送者和请求的处理者解耦了
    • 扩展性强,满足开闭原则。可以根据需要增加新的请求处理类
    • 灵活性强。可以动态地改变链内的成员或者改变链的次序来适应流程的变化
    • 简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,避免了使用众多的条件判断语句
    • 责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的职责范围,符合类的单一职责原则
  • 缺点

    • 不能保证每个请求一定被处理,该请求可能一直穿到链的末端都得不到处理
    • 如果责任链过长,请求的处理可能涉及多个处理对象,系统性能将受到一定影响
    • 责任链建立的合理性要靠客户端保证,增加了客户端的复杂性,可能会由于责任链拼接次序错误而导致系统出错,比如可能出现循环调用

4. 责任链模式练习题(多选)

  • 5 个海盗抢得 100 枚金币,他们的代号为 ABCDE,他们将按照字母顺序依次提出金币的分配方案。首先由 A 提出分配方案,然后 5 人表决,如果支持此方案的人数不超过投票人数的一半,那么 A 将被扔入大海喂鲨鱼,然后由 B 继续提方案,依此类推。假定每个海盗都绝顶聪明,并且相当理智,那么海盗 A 应该提出怎样的分配方案才能够使自己的收益最大化?

    1. ABCDE 分别得 20、20、20、20、20
    2. ABCDE 分别的 0、25、25、25、25
    3. ABCDE 分别得 97、0、1、0、2
    4. ABCDE 分别得 97、0、1、2、0

题目解析

倒着推,因为规则是超过投票人数一半才不会死,所以如果只剩下 DE 两人,D 提出任何方案都会被 E 反对,然后被 E 独得 100 金币。D 知晓这一点,所以 D 一定会保 C 不死,C 知道 D 一定会帮自己,所以 C 一定会把 100 个金币都收走,DE 什么都得不到。B 知道这一点,所以 B 只需要给 D 和 E 每人一个金币,剩下 98 个给自己。因为对于支持 C 而言,支持 B 收益更多,所以 D 和 E 都会支持 B 的提议。所以对于 A 来说,A 只需要多给 D 或者 E 一枚金币,再给什么都没有的 C 一枚金币,让这两个人同意自己的方案即可(貌似跟责任链模式关系并不明显)

-------------------- 本文结束感谢您的阅读 --------------------