职责链模式其实很好理解,由于一个链字出卖了它的灵魂。我们可以从这个字得到很大的提示。首先这个模式一定有传递性,而且,节点是可以重复拼接的,并且每个节点都具有一定的过滤功能,一定的职责。
是不是想起了组合模式里的一些内容呢? 是的,他们两个有着天然的类似点,不过组合模式主要职责是给执行者添加一些列行为,而不区分内部的执行。职责链模式则会强调内部的细节,他可以手动传递权限,手动终止权限。
举个栗子吧:
小时候,俺是一个学渣,平时作业都不会做,但是老师硬性要求你做。没办法,只有去借鉴学霸的作业。首先,我们班人都超级好,我做在最后一排,然后
我问前一排的妹纸: 嗨,小芳,你的作业能给我看看嘛?
小芳: 我作业没做呢?我帮你问问前面的。
小芳: 小明,你作业做完了吗?能给我看看嘛?
小明: 我作业没做呢?我帮你问问前面的。
小明: 小吉,你作业做完了吗?能给我看看嘛?
小吉: 做完了,给你吧。
恩,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);
恩,以上结果只是一个简单地示范。 这里需要提个醒,职责链模式是设计模式中最容易忘记的模式之一,因为它好用到不叫模式。所以,职责链模式的用法也是很多的,希望大家多多探索,将自己学到的只是分享出来,这是,极好的呀!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。