前言
作为一个前端切图仔
,少有和各类设计模式打交道。但这不影响我们学习设计模式的思维,来提升我们的代码水平。
本章通过讲解职责链模式
,希望能够让你对设计模式更一步的学习。
本章学习内容👇
- 灵活可拆分的职责链模式
灵活可拆分的职责链模式
在上一章,《JavaScript职责链模式与开发实践(中)》
我们将大段的条件判断函数拆分为了三个小函数,并用职责链模式
模式串联起来了这些模块。虽然解决了大量使用条件分支的语句,但这依然还存在一些问题:
function orderMember(seniorMember, price) {if (seniorMember) {return orderSeniorMember(price)} else {return price * 0.95}
}
可以看到,在职责链
的传递上非常僵硬,链上的下一段函数直接被耦合在了业务函数之中。这是违反开放-封闭原则(OCP
,Open Closed Principle)是所有面向对象原则的核心。如果以后我们的会员还要再分钻石会员
以及大会员
等等就意味着必须改动这些业务函数内部。这会扰乱我们之前所构建的链条
,就像在已经创建好的数组我们去改变其中每一个元素的顺序而导致整个数组偏移的后果。
接下来, 我们通过对职责链
节点进行一层工厂化包装来解决这个问题👇
首先,我们先解除之前三个函数节点
的耦合。并在需要传递给下一层请求时,我们通过返回一个字符串nextSuccessor
表示。
function orderNormal(userType, seniorMember, stock, price) {if (userType === 0) {if (stock > 0) {console.log(price * 0.99)} else {console.log(false)}} else {return 'nextSuccessor'; //向职责链后继节点传递请求}
}
function orderMember(userType, seniorMember, stock, price) {if (seniorMember == 0) {return 'nextSuccessor'; //向职责链后继节点传递请求} else {console.log(price * 0.95)}
}
function orderSeniorMember(userType, seniorMember, stock, price) {if (userType === 1 && seniorMember == 1) {console.log(price * 0.91)}
}
接着, 我们定义一个构造函数Chain
,使它接受一个函数作为该节点运行的函数,同时他还拥有俩个属性和方法:
fn
当前的运行节点函数successor
下一个节点
Chain.prototype.setNextSuccessor
指定在链中的下一个节点Chain.prototype.passRequest
传递请求给某个节点
let Chain = function (fn) {this.fn = fn;this.successor = null;
};
Chain.prototype.setNextSuccessor = function (successor) {return this.successor = successor;
};
Chain.prototype.passRequest = function () {var ret = this.fn.apply(this, arguments);if (ret === 'nextSuccessor') {return this.successor && this.successor.passRequest.apply(this.successor, arguments);}return ret;
};
然后, 我们通过这个工厂实例化包装下我们的节点,并测试运行。
var chainOrderSenior = new Chain(orderSeniorMember);
var chainOrderMember = new Chain(orderMember);
var chainOrderNormal = new Chain(orderNormal);
chainOrderMember.setNextSuccessor(chainOrderSenior);
chainOrderNormal.setNextSuccessor(chainOrderMember);
chainOrderNormal.passRequest(1, false, 1, 100)
控制台输出
95
完成🤩。这样下次我们需要追加新的会员等级或者其他条件时,只需要设置在对应节点设置后继节点即可。
\