簡介
職責鏈模式(Chain of responsibility)是使多個物件都有機會處理請求,從而避免請求的傳送者和接受者之間的耦合關係。將這個物件連成一條鏈,並沿著這條鏈傳遞該請求,直到有一個物件處理他為止。 也就是說,請求以後,從第一個物件開始,鏈中收到請求的物件要麼親自處理它,要麼轉發給鏈中的下一個候選者。提交請求的物件並不明確知道哪一個物件將會處理它——也就是該請求有一個隱式的接受者(implicit receiver)。根據執行時刻,任一候選者都可以響應相應的請求,候選者的數目是任意的,你可以在執行時刻決定哪些候選者參與到鏈中。
案例一
果伯伯在水果市場賣蘋果,果果老闆說:蘋果分優質,中等,一般,給錢就賣四大類蘋果。 優質蘋果5元一斤,中等蘋果4元一斤,一般蘋果3元一斤,給錢就賣蘋果1元一斤,一次最少賣一斤(這是例子,現實中不是這樣的) 小明拿5元錢在這個攤上買1斤蘋果, 小亮拿4元錢也在這個攤上買1斤蘋果, 小帥拿3元錢也在這個攤上買1斤蘋果, 小七拿1元錢也在這個攤上買1斤蘋果,
分析:
小明、小亮、小帥、小七都是拿錢去買蘋果的,他們就好比【一個個的請求】;
果伯伯的攤位就可以當做一個職責鏈,處理不同的買方請求。
蘋果價位:price=5,4,3,1,
小明、小亮、小帥、小七的錢:people$=5,4,3,1
複製程式碼
第一個版本:
/**
* 買蘋果
* @name {string} 姓名
* @price {Number} 價格
* @weight {Number} 重量
*
* */
function buyapple(name,price,weight){
if(price==5){
console.log(name+'買到了5元1斤的蘋果');
}else if(price==4){
console.log(name+'買到了4元1斤的蘋果');
}else if(price==3){
console.log(name+'買到了3元1斤的蘋果');
}else if(price==1){
console.log(name+'買到了1元1斤的蘋果');
}
}
buyapple('小明',5,1);
buyapple('小亮',4,1);
buyapple('小帥',3,1);
buyapple('小七',1,1);
複製程式碼
上述程式碼也許是最簡單程式碼,最初級的程式設計師都會寫,但是這樣寫的程式碼if巢狀比較多,更改維護比較費勁。假設又有2元一斤的蘋果與使用者,那就需要更改原始碼了。
第二個版本:
function buyapple5(name,price,weight){
if(price==5){
console.log(name+'買到了5元1斤的蘋果');
}else{
return 'next';
}
}
function buyapple4(name,price,weight){
if(price==4){
console.log(name+'買到了4元1斤的蘋果');
}else{
return 'next';
}
}
function buyapple3(name,price,weight){
if(price==3){
console.log(name+'買到了3元1斤的蘋果');
}else{
return 'next';
}
}
function buyapple1(name,price,weight){
if(price==1){
console.log(name+'買到了1元1斤的蘋果');
}else{
return 'next';
}
}
function Buyapple(fn){
this.fn=fn;
this.nextfn=null;//把下一個執行設定為null
}
Buyapple.prototype.setNextfn=function(nextfn){
return this.nextfn=nextfn;//設定下一個執行的函式
}
Buyapple.prototype.Nextfn=function(){
var res = this.fn.apply(this,arguments);//執行當前函式
if( res === "next" ){
//判斷當前函式是否有下一個執行函式,有則執行
return this.nextfn && this.nextfn.Nextfn.apply(this.nextfn,arguments);
}
}
//現在我們吧3個訂單函式分別包裝成職責鏈的節點
var chainOrder5 = new Buyapple(buyapple5);
var chainOrder4 = new Buyapple(buyapple4);
var chainOrder3 = new Buyapple(buyapple3)
var chainOrder1 = new Buyapple(buyapple1);
//然後指定節點在指責鏈的順序
chainOrder5.setNextfn(chainOrder4);
chainOrder4.setNextfn(chainOrder3);
chainOrder3.setNextfn(chainOrder1);
//最後傳遞請求 最上層,過濾資料執行
chainOrder5.Nextfn('小帥',3,1);
複製程式碼
目前的職責鏈能夠保證按照順序執行下去。可以隨意組合。但現在的程式碼只能保證是同步執行的。如果有非同步中間執行程式碼則無法執行。
第三個版本:
function buyapple5(name,price,weight){
if(price==5){
console.log(name+'買到了5元1斤的蘋果');
}else{
return 'next';
}
}
function buyapple4(name,price,weight){
var that=this;
if(price==4){
console.log(name+'買到了4元1斤的蘋果');
}
setTimeout(function(){
console.log('等待兩秒後執行');
that.next(name,price,weight);
},2000);
}
function buyapple3(name,price,weight){
if(price==3){
console.log(name+'買到了3元1斤的蘋果');
}else{
return 'next';
}
}
function buyapple1(name,price,weight){
if(price==1){
console.log(name+'買到了1元1斤的蘋果');
}else{
return 'next';
}
}
function Buyapple(fn){
this.fn=fn;
this.nextfn=null;//把下一個執行設定為null
}
Buyapple.prototype.setNextfn=function(nextfn){
return this.nextfn=nextfn;//設定下一個執行的函式
}
Buyapple.prototype.Nextfn=function(){
var res = this.fn.apply(this,arguments);//執行當前函式
if( res === "next" ){
//判斷當前函式是否有下一個執行函式,有則執行
return this.nextfn && this.nextfn.Nextfn.apply(this.nextfn,arguments);
}
}
Buyapple.prototype.next=function(){
//非同步函式是無法返回next,只有藉助next函式觸發執行
return this.nextfn && this.nextfn.Nextfn.apply(this.nextfn,arguments);
}
//現在我們吧3個訂單函式分別包裝成職責鏈的節點
var chainOrder5 = new Buyapple(buyapple5);
var chainOrder4 = new Buyapple(buyapple4);
var chainOrder3 = new Buyapple(buyapple3)
var chainOrder1 = new Buyapple(buyapple1);
//然後指定節點在指責鏈的順序
chainOrder5.setNextfn(chainOrder4);
chainOrder4.setNextfn(chainOrder3);
chainOrder3.setNextfn(chainOrder1);
//最後傳遞請求 最上層,過濾資料執行
chainOrder5.Nextfn('小帥',3,1);
複製程式碼
connect框架next函式
var i=0,//索引
_arrycallback=[];//儲存函式
//執行下一個函式
function next(){
i++;
_arrycallback[i] && _arrycallback[i]();
}
//儲存next函式
function push_next(fn){
_arrycallback.push(fn);
};
push_next(function(){
console.log("1");
next();
});
push_next(function(){
setTimeout(function(){
console.log("2","延遲500毫秒輸出");
next();
},3000)
});
push_next(function(){
console.log("3");
next();
});
push_next(function(){
console.log("4");
next();
});
//執行
_arrycallback[0]();
複製程式碼
Node.js 中大名鼎鼎的connect框架正是這樣實現中介軟體佇列的。有興趣可以去看看它的原始碼或者這篇解讀《何為 connect 中介軟體》。
自己寫的一個外掛
//更新時間:2017-12-03
//參考連結:https://juejin.im/post/5a06c6d051882555cb19416b
複製程式碼
js函式整合佇列順序執行外掛 https://juejin.im/post/5a06c6d051882555cb19416b
總結
一:職責鏈模式的最大優點就是解耦了請求必送者和N個接收者之間的複雜關係,由於不知道鏈中的如個節點可以處理你發現的請求,所以你只需要請求傳遞給第一個節點即可。 二:使用了職責鏈模式之後,鏈中的節點物件可以靈活地拆分重組。增加或者刪除一個節點,或者改變節點在鏈中的位置都是輕而易舉的事情。 三:在職責模式還有一個優點,那就是可以手動指定起始節點,請求並不是非得從鏈中的第一個節點開始傳遞。 四:職責鏈模式使得程式中多了一些節點物件,可能在某一次的請求過程中,大部分節點並沒有起到實質性的作用,它們的作用僅僅是讓請求傳遞下去,從效能方面考慮,我們要避免過長的職責鏈帶來效能損耗。 五:如果一個節點出錯,就會導致整個鏈條斷開。