目录
职责链模式的定义
职责链模式:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
职责链中的节点数量和顺序是可以自由变化的,我们可以在运行时决定链中包含哪些节点。
现实场景:公交上传递刷卡
使用场景:无论是作用域链、原型链、还是DOM节点中的事件冒泡,我们都能从中找到职责链的影子。
优点:解耦了请求发送者和N个接受者之间的复杂关系,可以手动指定起始节点
缺点:不能保证某个请求一定会被链中的节点处理
演示范例1 —— 职责链模式实现电商优惠劵发放
一个售卖手机的电商网站:针对预定用户实行优惠,支付过500元定金的用户会收到100元的商城优惠券,支付过200元定金的用户会收到50元的商城优惠券,没有支付定金的用户归为普通购买,且在库存有限的情况下不一定保证买到。
- orderType:表示订单类型(定金用户或者普通购买用户),code的值为1的时候是500元定金用户,为2的时候是200元定金用户,为3的时候是普通购买用户。
- pay:表示用户是否已经支付定金,值为true或者false,虽然用户已经下过500元定金的订单,但如果他一直没有支付定金,现在只能降级进入普通购买模式。
- stock:表示当前用于普通购买的手机库存数量,已经支付过500元或者200元定金的用户不受此限制。
传统方式实现:
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);
职责链模式实现:
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";
}
};
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);
此处利用JavaScript的函数式特性改写了Function.prototype.after函数,虽然这种用AOP(面向切面编程)来实现职责链既简单又巧妙,但这种把函数叠在一起的方式,同时也叠加了函数的作用域,如果链条太长的话,也会对性能有较大的影响。
演示范例2 —— 职责链模式实现文件上传对象
// 是否支持flash
function supportFlash() {
var hasFlash = 0; //是否安装了flash
var flashVersion = 0; //flash版本
if (document.all) {
var swf = new ActiveXObject('ShockwaveFlash.ShockwaveFlash');
if (swf) {
hasFlash = 1;
VSwf = swf.GetVariable("$version");
flashVersion = parseInt(VSwf.split(" ")[1].split(",")[0]);
}
} else {
if (navigator.plugins && navigator.plugins.length > 0) {
var swf = navigator.plugins["Shockwave Flash"];
if (swf) {
hasFlash = 1;
var words = swf.description.split(" ");
for (var i = 0; i < words.length; ++i) {
if (isNaN(parseInt(words[i]))) continue;
flashVersion = parseInt(words[i]);
}
}
}
}
return { f: hasFlash, v: flashVersion };
}
function getActiveUploadObj() {
try{
return new ActiveObject("TXFTNActiveX.FTNUpload"); // IE上传控件
}catch(e) {
return "nextSuccessor";
}
}
function getFlashUploadObj() {
if(supportFlash().f === 1) {
var str = '<object type="application/x-shockwave-flash"></object>';
return $(str).appendTo($("body"));
}
return "nextSuccessor";
}
function getFormUploadObj() {
var str = '<input name="file" type="file" class="ui-file" />';
return $(str).appendTo($("body"));
}
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 getUploadObj = getActiveUploadObj.after(getFlashUploadObj).after(getFormUploadObj);
console.log(getUploadObj());
可以和迭代器模式的文件上传进行对比 https://blog.csdn.net/weixin_41192489/article/details/116020037
更多设计模式详见——js设计模式【详解】总目录
https://blog.csdn.net/weixin_41192489/article/details/116154815