该如何理解职责链设计模式

170 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第5天,点击查看活动详情

前情提要

相信很多jym在软件开发的生涯中从开始到现在就总是能听到设计模式,各种设计模式。但是在实际中真正用到的,或者真正对设计模式有很深的理解的很少。甚至有一小部分人对具体的设计模式闻所未闻,更别提在项目中使用了。但是在更高层次的,比如框架等几乎全是各种设计模式的使用,所以要想开发的路走的更远,深刻学习设计模式避无可避...

天天说设计模式,那到底什么是设计模式?

  • 设计模式是软件开发过程中通用的解决方案
  • 更为准确的定义:是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。”

那什么又是职责链模式?

  • 职责链设计模式又称责任链模式。
  • 属于行为型设计模式之一
  • 职责链就像火车、高铁的车厢,一节一节的通过钩子进行挂接。在不关心车厢号顺序以及两端必须为车头的情况下他们可以进行任意进行排列组合。
  • 而每一结车厢只关心自己的事情,做到了高内聚低耦合。
  • 对于职责链的模式的理解举一个不是非常恰当的例子:就像我们拼的一个乐高或者积木。之前拼装过一个小狗和一个大苹果。最小颗粒度是一个一个小小的上面是圆形凸起,下面是圆形凹陷的零件。你可以选择一个一个的小零件从底部开始去拼。但是问题是你在数量足够庞大,像我拼的那个大苹果有几百个零件的时候,非常容易拼错而且对比原型图时也更加费劲。而如果我们根据分层图,一层一层的单独拼好,无论先拼最上面的还是中间的还是最下面的都无所谓。最后再根据顺序一层一层的直接堆上去。
  • 之所以讲上面那个例子不是很恰当,是因为职责链中应当满足每一个环节都允许随意的排列组合。而拼乐高积木啥的在开始之初或者说被设计的时候其顺序就已经确定了,是无法自定义顺序的。

日常开发中的应用场景及实践

概念性的东西千篇一律,讲再多眼睛脑子说会了,手说你放屁。

  • 场景:需要我们实现网吧的充值返现活动,充1000返500;充500返100;充100返10块。
  • 实现:
    • 常规思维的实现方式
    • 思路:通过if-else来实现
      function recharge(money) {
          if (money === 1000) return money + 500;
          else if(money === 500) return money + 100;
          else if (money === 100) return money + 10;
      }
      recharge(100) // 110
      
    • 职责链模式
    • 思路:通过职责链模式,针对每个金额单独封装逻辑实现高内聚。然后开发者可根据产品经理的需求灵活的进行各个节点的组合。
      // 声明充值类
      class Recharge {
          constructor(fn) {
              this.fn = fn;
              this.next = null
          }
          // 为充值节点设置下一个节点
          setNextFn(next) {
              if(next) this.next = next;
          }
          // 节点的开始函数
          start(money) {
              const result = this.fn(money);
              console.log({result});
              if(result === 'next' && this.next) this.next.start(money);
              else return result;
          }
      }
      // 定义1000节点的逻辑方法
      function recharge_1000(money) {
          if (money == 1000) {
              console.log(`充值总金额为:${ money + 500 }`);
          } else {
              return 'next';
          }
      }
      // 定义500节点的逻辑方法
      function recharge_500(money) {
          if (money == 500) {
              console.log(`充值总金额为:${ money + 100 }`);
          } else {
              return 'next';
          }
      }
      // 定义100节点的逻辑方法
      function recharge_100(money) {
          if (money == 100) {
              console.log(`充值总金额为:${ money + 10 }`);
          } else {
              return 'next';
          }
      }
      // 定义默认节点的逻辑方法
      function recharge_default(money) {
          return result = money;
      }
      // 对每个节点进行实例化,同时为每个节点挂载启动和设置下一个节点的方法 
      const nodeOfRecharge_1000 = new Recharge(recharge_1000);
      const nodeOfRecharge_500 = new Recharge(recharge_500);
      const nodeOfRecharge_100 = new Recharge(recharge_100);
      const nodeOfRecharge_default = new Recharge(recharge_default);
      // 开始为每个节点进行下一个节点挂载。形成一个连式结构
      nodeOfRecharge_1000.setNextFn(nodeOfRecharge_500);
      nodeOfRecharge_500.setNextFn(nodeOfRecharge_100);
      nodeOfRecharge_100.setNextFn(nodeOfRecharge_default);
      // 从任意节点开始进行方法执行测试
      nodeOfRecharge_1000.start(500) // 充值总金额为:600
      nodeOfRecharge_1000.start(1000) // 充值总金额为:1500
      nodeOfRecharge_500.start(100) // 充值总金额为:110
      nodeOfRecharge_100.start(10000) // 充值总金额为:10000
      

总结

  • 从上述的案例对比可以看出,当逻辑比较少的时候if-else方式在代码量上更具优势。
  • 但是在实际面向C端的业务逻辑中基本上不可能存在如此少的逻辑。当代码量上去后if-else的方式便显得没有那么容易维护。而且每次改动都需要对recharge方法进行重构。当活动取消的时候还得把该方法隐藏或者删除,无法为未来做准备。
  • 而职责链模式可以做到逻辑改动只需要改动对应的节点方法,提交测试时也无须让测试测试其他功能,只需要测试这一个节点即可。为测试省下了工作量。如果只是节点的改动,则改动代码就更少了。甚至可以将该规则通过和后端配合实现动态配置,完全实现功能开发完成后,后续改动与开发人员无关,并且逻辑清晰,对于可能出现的问题定位的效率极其有帮助
  • jym如果有其他的见解,欢迎在评论区里打出来