传递请求之职责链模式

职责链模式其实很好理解,因为一个链字出卖了它的灵魂。咱们能够从这个字获得很大的提示。首先这个模式必定有传递性,并且,节点是能够重复拼接的,而且每一个节点都具备必定的过滤功能,必定的职责。编程

是否是想起了组合模式里的一些内容呢? 是的,他们两个有着自然的相似点,不过组合模式主要职责是给执行者添加一些列行为,而不区份内部的执行。职责链模式则会强调内部的细节,他能够手动传递权限,手动终止权限。设计模式

举个栗子吧:app

小时候,俺是一个学渣,平时做业都不会作,可是老师硬性要求你作。没办法,只有去借鉴学霸的做业。首先,咱们班人都超级好,我作在最后一排,而后函数

我问前一排的妹纸: 嗨,小芳,你的做业能给我看看嘛?
小芳: 我做业没作呢?我帮你问问前面的。
小芳: 小明,你做业作完了吗?能给我看看嘛?
小明: 我做业没作呢?我帮你问问前面的。
小明: 小吉,你做业作完了吗?能给我看看嘛?
小吉: 作完了,给你吧。

恩,good,事情圆满解决。完美的体现出,职责链的内涵,上一节点,只要知道下一个节点的接口,so that enough。 若是自己节点可以完成任务,则将结果输出,终止传递。性能

用代码标识即为:this

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的冲动(不是写的烂,是写的太烂了),惟一可以表扬他的就是,知道职责链模式的原理。因此为了情怀,咱们须要给上面的代码换一身皮.prototype

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必须能明确的标识下一个到底继不继续。code

固然,要知道,这个职责链模式并非必定要把管理权交给内部执行,你固然也能够在外面进行判断和设置。接口

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);

恩,以上结果只是一个简单地示范。 这里须要提个醒,职责链模式是设计模式中最容易忘记的模式之一,由于它好用到不叫模式。因此,职责链模式的用法也是不少的,但愿你们多多探索,将本身学到的只是分享出来,这是,极好的呀!

相关文章
相关标签/搜索