职责链模式其实很好理解,由于一个链字出卖了它的灵魂。我们可以从这个字得到很大的提示。首先这个模式一定有传递性,而且,节点是可以重复拼接的,并且每个节点都具有一定的过滤功能,一定的职责。
是不是想起了组合模式里的一些内容呢? 是的,他们两个有着天然的类似点,不过组合模式主要职责是给执行者添加一些列行为,而不区分内部的执行。职责链模式则会强调内部的细节,他可以手动传递权限,手动终止权限。
举个栗子吧:
小时候,俺是一个学渣,平时作业都不会做,但是老师硬性要求你做。没办法,只有去借鉴学霸的作业。首先,我们班人都超级好,我做在最后一排,然后
我问前一排的妹纸: 嗨,小芳,你的作业能给我看看嘛?小芳: 我作业没做呢?我帮你问问前面的。小芳: 小明,你作业做完了吗?能给我看看嘛?小明: 我作业没做呢?我帮你问问前面的。小明: 小吉,你作业做完了吗?能给我看看嘛?小吉: 做完了,给你吧。
恩,good,事情圆满解决。完美的体现出,职责链的内涵,上一节点,只要知道下一个节点的接口,so that enough。 如果本身节点能够完成任务,则将结果输出,终止传递。
用代码标识即为:
function Me (flag){ if(flag===1){ console.log("I can do this homeword"); }else{ console.log("I can't :(, but u can do this ?"); XiaoFang(flag); }}function XiaoFang (flag){ if(flag===1){ console.log("I can do this homeword"); }else{ console.log("I can't :(, but u can do this ?"); XiaoJi(flag); }}function XiaoJi (flag){ if(flag===0){ console.log("I can do this homeword"); }else{ console.log("I can't :(, but u can do this ?"); //...继续询问下一个人 }}Me(0);
没错,职责链的主要内涵就是,如果你不行,在问问下一个人行不行。。。但是上面代码让我有种想kill people的冲动(不是写的烂,是写的太烂了),唯一能够表扬他的就是,知道职责链模式的原理。所以为了情怀,我们需要给上面的代码换一身皮.
function Chain(fn){ this.fn = fn; this.nextExer = null;}Chain.prototype.setNext = function(obj){ this.nextExer = obj;}Chain.prototype.exe = function(flag){ var result = this.fn.apply(this,arguments); if(result === "next"){ this.next(flag); }}Chain.prototype.next = function(){ return this.nextExer.exe.apply(this.nextExer,arguments)}var fn1 = new Chain(function(flag){ if(flag===1){ console.log("I can do this homework"); }else{ console.log("I can't do this homework"); return "next"; }});var fn2 = new Chain(function(flag){ if(flag===1){ console.log("I can do this homework"); }else{ console.log("I can't do this homework"); return "next"; }})var fn3 = new Chain(function(flag){ if(flag===0){ console.log("I can do this homework"); }else{ console.log("I can't do this homework"); return "next"; }})fn1.setNext(fn2);fn2.setNext(fn3);fn1.exe(0);
虽然,上面这段代码看起来清晰很多,使用next调用下一个函数,使用exe初始化.但是看起来在setNext哪里有点啰嗦。我们试着改进:
Chain.prototype.setNext = function(obj){ this.nextExer = obj; return obj;}fn1.setNext(fn2).setNext(fn3);fn1.exe(0);
只需要将setNext哪里返回一个Obj,就可以得到完美的链式调用了。可以从上面的代码中看出一些端倪,在职责链模式中,我们需要规定,在每个exe执行过后需要设置一个result,并且这个result必须能明确的标识下一个到底继不继续。
当然,要知道,这个职责链模式并不是一定要把管理权交给内部执行,你当然也可以在外面进行判断和设置。
var fn2 = new Chain(function(flag){ console.log("I can't do this homework"); this.nextExer.fn(0); //手动执行下一个})
通过上面的步骤,可以在外部直接判断,是否执行下一个。所以职责模式的写法也是很多的。
职责链的利弊
而且,职责链最大的一个好处就是,你可以从链中,任意一个节点开始遍历。 我们用上面那个例子体会一下。
假如,我前面座的童鞋,我和他都同时喜欢一女生,所以我俩关系超差。我当然不能问情敌要作业啦,这时候,我可以再往前一个同学问。利用职责模式就为.
xiaoMing.setNext(xiaoFang).setNext(xiaoJi);//改写,直接从小芳开始xiaoFang.setNext(xiaoJi);
这应该算是职责链模式的一大特色,但是这个也不是没有问题的,就是我们需要在最后一个节点上加上判断,表示如果没有其他处理程序,而且在该节点上也不成立的话,则需要抛出一个错误,或者做出相应的说明. 并且,我们每次请求的时候,都会从节点链的开始进行遍历,这样很可能会造成性能的损失,所以这里需要注意的是,不要设置太长的职责链。
使用AOP
这里AOP指的是面向切面编程,即将其他函数动态的加入到一个函数中,比如before & after. 我们仔细想想,一个队列无外乎就是在前在后的关系,所以一个before和after已经算是万能的了(排除你有动态删除的需求)。
Function.prototype.after = function(fn){ var _this = this; return function(){ var res = _this.apply(this,arguments); if(!res){ //值为Boolean return fn.apply(this,arguments); } return res; }}Function.prototype.before = function(fn){ var _this = this; return function(){ fn.apply(this,arguments); return _this.apply(this,arguments); }}
上面已经将AOP中两个最重要的before和after添加到Function的原型里面了。
现在我们可以使用这两把三相之力开启职责链模式
XiaoMing.after(XiaoFang).after(XiaoJi);
我操,完美啊,通俗易懂哎喂。
如果我们需要加上判断的话,可以直接在after和before里面写
//只举before的例子吧Function.prototype.before = function(fn){ var _this = this; return function(){ var res = fn.apply(this,arguments); //值为Boolean,表示是否继续向下传递 if(res===false){ //如果返回不成立,则继续向下传递 return _this.apply(this,arguments); } }}function Xiaoming(){ console.log("I can do this homework"); return "ok"; //中断返回,当然这里你可以随便定义,除了"next"}function XiaoFang(){ console.log("I can't do this homework"); return "next";}Xiaoming. before(XiaoFang)();
职责链模式之干货
我们这里再次回忆一下职责链模式的用处,将一个请求依照一条链传递,如果有个满足则断开传递,返回结果。 想一想,这个和我们的迭代器模式有着异曲同工的妙处,迭代器模式同样也是遍历选出最优解,但是相比而言,职责链模式的直观性个书写的幸福感是远远超过迭代器模式的。
在写一些hacks的时候,难免会用到if...else if...判断语句,上次我们使用迭代器模式完成这样的功能,但是效果不是很理想,这里我们使用职责链模式完成。
事件模式的选择函数
Function.prototype.after = function(fn){ var _this = this; return function(){ var res = _this.apply(this,arguments); if(res==="next"){ //值为Boolean return fn.apply(this,arguments); } return res; }}var bind = (function() { var DOM2 = function() { if (document.addEventListener) { return function(ele, fn, type) { ele.addEventListener(type, () => { fn(); }, false); } } else { return "next"; } }; var IE = function() { if (document.attachEvent) { return function(ele, fn, type) { ele.attachEvent(type, fn); } } else { return "next"; } }; var DOM0 = function(){ return function(ele, fn, type) { ele[`on${type}`] = () => { fn(); }; } } return DOM2.after(IE).after(DOM0)();})();console.log(bind);
恩,以上结果只是一个简单地示范。 这里需要提个醒,职责链模式是设计模式中最容易忘记的模式之一,因为它好用到不叫模式。所以,职责链模式的用法也是很多的,希望大家多多探索,将自己学到的只是分享出来,这是,极好的呀!