责任链
什么是责任链
职责链模式的定义是:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
- 举个栗子:
- 当公交车很挤的时候,你从后门上车,这个时候你不可能直接把硬币放到收款箱里面, 因为你不知道它在哪,那你就只能把硬币给你前面一个人,让他帮你传到前面一个人手上,这样一直传递到站在收款箱旁边人的手上,由他把硬币放到收款箱里面。
JavaScript实现职责链模式
Function.prototype.after = function(fn) {
var _self = this;
return function () {
// 将self的执行上下文改成window并执行函数
var ret = _self.apply(this, arguments);
// 如果函数的返回值是 nextSuccessor 则执行传参函数
if(ret === "nextSuccessor") {
// 将fn的执行上下文改成window并执行函数
return fn.apply(this, arguments);
}
return ret;
}
}
在函数执行之后确定是否执行下一个函数,你每次调用after,都相当于在已有函数之后添加一个函数,至于是否执行后面这个函数,取决于前一个函数的返回值。
示例
假设这么一个场景:
一个售卖手机的电商网站,经过分别缴纳500元定金和200元定金的两轮预定后,到了正式购买阶段。针对预定用户实行优惠,支付过500元定金的用户会收到100元的商城优惠券,支付过200元定金的用户会收到50元的商城优惠券,没有支付定金的用户归为普通购买,且在库存有限的情况下不一定保证买到。
传统方式实现
/* 传统方式实现 */
// orderType:[1:500, 2:200, 3:普通],isPaid:true/false,stock:库存量
var order = function(orderType, isPaid, stock) {
if(orderType === 1) {
if(isPaid) {
console.log("500元定金预购,得到100优惠券");
} else {
if(stock > 0) {
console.log("普通购买,无优惠券");
}else {
console.log("库存不足");
}
}
}else if(orderType === 2) {
if(isPaid) {
console.log("200元定金预购,得到50优惠券");
} else {
if(stock > 0) {
console.log("普通购买,无优惠券");
}else {
console.log("库存不足");
}
}
}else if(orderType === 3) {
if(stock > 0) {
console.log("普通购买,无优惠券");
}else {
console.log("库存不足");
}
}
}
order(1, true, 500);
可以看到耦合性相当的高,orderType === 3
条件下的代码在orderType === 1
和orderType === 2
条件下都有用到。而且当还有另一种又会方案的时候,需要修改order()的内容。当增加多种方案之后order()内将会有很多if
和else if
。这样的代码相当难看。
责任链方式实现
var order500 = function(orderType, isPaid, stock) {
if(orderType === 1 && isPaid === true) {
console.log("500元定金预购,得到100优惠券");
}else {
return "nextSuccessor";
}
};
var order200 = function(orderType, isPaid, stock) {
if(orderType === 2 && isPaid === true) {
console.log("200元定金预购,得到50优惠券");
}else {
return "nextSuccessor";
}
};
// orderType === 3的情况,也是默认其他种乱输入的情况
var orderNormal = function(orderType, isPaid, stock) {
if(stock > 0) {
console.log("普通购买,无优惠券");
}else {
console.log("库存不足");
}
};
Function.prototype.after = function(fn) {
var self = this;
return function() {
var ret = self.apply(this, arguments);
if(ret === "nextSuccessor") {
return fn.apply(this, arguments);
}
return ret;
};
}
var order = order500.after(order200).after(orderNormal);
order(1, true, 10);
优缺点
优点
- 降低耦合度
- 动态组合职责
- 责任链模式会把功能处理分散到单独的职责对象中,然后在使用的时候,就可以动态组合职形成职责链,从而可以灵活地给对象分配职责,也可以灵活地实现和改变对象的职责。
缺点
- 不能保证某个请求一定会被链中的节点处理
- 责任链模式的每个职责对象只负责处理自己处理的那部分,因此可能会出现某个请求,把整个责任链传递完也没有职责对象处理它。这就需要在使用责任链的时候,需要提供默认的处理。
我的项目通过责任链修改
##没有使用责任链前
if (value === 'check') {
/* 是否输入确认密码了 */
if (!this.checkPassword) {
this.inputErrorMessage('errorCheck', 'errorCheckText', '请确认密码')
return false
}
/* 两次密码是否一致 */
if (this.password !== this.checkPassword) {
this.inputErrorMessage('errorCheck', 'errorCheckText', '两次密码输入不一致')
return false
}
this.errorCheck = false
} else if (value === 'pass') {
/* 是否输入密码了 */
if (!this.password) {
this.inputErrorMessage('errorPass', 'errorPassText', '请输入密码')
return false
}
/* 密码格式是否正确 */
let reg = /(?!^(\d+|[a-zA-Z]+|[~!@#$%^&*?]+)$)^[\w~!@#$%&*?/]+$/
if (!reg.test(this.password)) {
this.inputErrorMessage('errorPass', 'errorPassText', '请输入正确密码格式')
return false
}
// 输入确认密码后又修改密码
if (this.password !== this.checkPassword) {
if (this.checkPassword) {
this.inputErrorMessage('errorCheck', 'errorCheckText', '两次密码输入不一致')
}
} else {
this.errorCheck = false
}
this.errorPass = false
}
使用责任链
passwordFunc (value) {
if (value === 'pass' && !this.password) {
this.inputErrorMessage('errorPass', 'errorPassText', '请输入密码')
} else {
return 'nextSuccessor'
}
},
passVerifyFunc (value) {
let reg = /(?!^(\d+|[a-zA-Z]+|[~!@#$%^&*?]+)$)^[\w~!@#$%&*?/]+$/
if (value === 'pass' && !reg.test(this.password)) {
this.inputErrorMessage('errorPass', 'errorPassText', '请输入正确密码格式')
} else {
if (value === 'pass') {
this.errorPass = false
}
return 'nextSuccessor'
}
},
samePassFunc (value) {
if (this.password !== this.checkPassword && this.password && this.checkPassword) {
this.inputErrorMessage('errorCheck', 'errorCheckText', '两次密码输入不一致')
} else {
return 'nextSuccessor'
}
},
checkPassFunc (value) {
if (value === 'check' && !this.checkPassword) {
this.inputErrorMessage('errorCheck', 'errorCheckText', '请确认密码')
} else {
return 'nextSuccessor'
}
},
checkOKFunc (value) {
this.errorCheck = false
},
handlePasswordCheck (value) {
let order = this.passwordFunc.after(this.passVerifyFunc).after(this.samePassFunc).after(this.checkPassFunc).after(this.checkOKFunc)
order(value)
}
有点点复杂…不是所有的if-else都适合用这个设计模式。所以打算去学习其他的设计模式了呢~