虽然已经把大函数拆分成了互不影响的3个小函数,但可以看到,请求在链条传递中的顺序非常僵硬,传递请求的代码被耦合在了业务函数之中

文章插图
这依然是违反开放——封闭原则的,如果有天要增加300元预订或者去掉200元预订,意味着就必须改动这些业务函数内部 。就像一根环环相扣打了死结的链条,如果要增加、拆除或者移动一个节点,就必须得先砸烂这根链条
灵活可拆分的职责链节点
下面采用一种更灵活的方式,来改进上面的职责链模式,目标是让链中的各个节点可以灵活拆分和重组
首先需要改写一下分别表示3种购买模式的节点函数,约定如果某个节点不能处理请求,则返回一个特定的字符串'nextSuccessor'来表示该请求需要继续往后面传递:

文章插图
接下来需要把函数包装进职责链节点,定义一个构造函数Chain,在new Chain的时候传递的参数即为需要被包装的函数,同时它还拥有一个实例属性this.successor,表示在链中的下一个节点 。此外Chain的prototype中还有两个函数,它们的作用如下所示:

文章插图
现在把3个订单函数分别包装成职责链的节点:

文章插图
通过改进,可以自由灵活地增加、移除和修改链中的节点顺序,假如某天网站运营人员又想出了支持300元定金购买,那就在该链中增加一个节点即可

文章插图
异步的职责链
上面的职责链模式中,让每个节点函数同步返回一个特定的值"nextSuccessor",来表示是否把请求传递给下一个节点 。而在现实开发中,经常会遇到一些异步的问题,比如要在节点函数中发起一个ajax异步请求,异步请求返回的结果才能决定是否继续在职责链中passRequet
这时候让节点函数同步返回"nextSuccessor"已经没有意义了,所以要给Chain类再增加一个原型方法Chain.prototype.next,表示手动传递请求给职责链中的下一个节点

文章插图
下面是一个异步职责链的例子

文章插图
现在得到了一个特殊的链条,请求在链中的节点里传递,但节点有权利决定什么时候把请求交给下一个节点 。可以想象,异步的职责链加上命令模式,可以很方便地创建一个异步ajax队列库
优缺点【Chain of Responsibility... JavaScript职责链模式】职责链模式的最大优点就是解耦了请求发送者和N个接收者之间的复杂关系,由于不知道链中的哪个节点可以处理发出的请求,所以只需把请求传递给第一个节点即可
在手机商城的例子中,本来要被迫维护一个充斥着条件分支语句的巨大的函数,在例子里的购买过程中只打印了一条log语句 。其实在现实开发中,这里要做更多事情,比如根据订单种类弹出不同的浮层提示、渲染不同的UI节点、组合不同的参数发送给不同的cgi等 。用了职责链模式之后,每种订单都有各自的处理函数而互不影响
其次,使用了职责链模式之后,链中的节点对象可以灵活地拆分重组 。增加或者删除一个节点,或者改变节点在链中的位置都是轻而易举的事情
职责链模式还有一个优点,那就是可以手动指定起始节点,请求并不是非得从链中的第一个节点开始传递 。比如在公交车的例子中,如果明确在前面的第一个人不是售票员,那当然可以越过他把公交卡递给他前面的人,这样可以减少请求在链中的传递次数,更快地找到合适的请求接受者 。这在普通的条件分支语句下是做不到的,没有办法让请求越过某一个if判断
拿代码来证明这一点,假设某一天网站中支付过定金的订单已经全部结束购买流程,在接下来的时间里只需要处理普通购买订单,所以可以直接把请求交给普通购买订单节点:
orderNormal.passRequest(1,false,500); //普通购买,无优惠券如果运用得当,职责链模式可以很好地帮助我们组织代码,但这种模式也并非没有弊端,首先不能保证某个请求一定会被链中的节点处理 。比如在期末考试的例子中,小纸条上的题目也许没有任何一个同学知道如何解答,此时的请求就得不到答复,而是径直从链尾离开,或者抛出一个错误异常 。在这种情况下,可以在链尾增加一个保底的接受者节点来处理这种即将离开链尾的请求
推荐阅读
- JavaScript命名空间常用方法
- Strategy Pattern JavaScript设计模式之策略模式
- JavaScript中变量和作用域
- JavaScript都得知道这3个数组方法
- javascript 创建对象常用几种方式
- JavaScript轻应用PWA实践全过程
- 8 个提高 JavaScript 性能的方法
- JavaScript获取json中key所对应的value值的简单方法
- JavaScript中执行上下文和执行栈
- 不可不知的 5 种 JavaScript 代码编辑器
