職責鏈模式

小小坤發表於2017-10-11

簡介

職責鏈模式(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個接收者之間的複雜關係,由於不知道鏈中的如個節點可以處理你發現的請求,所以你只需要請求傳遞給第一個節點即可。 二:使用了職責鏈模式之後,鏈中的節點物件可以靈活地拆分重組。增加或者刪除一個節點,或者改變節點在鏈中的位置都是輕而易舉的事情。 三:在職責模式還有一個優點,那就是可以手動指定起始節點,請求並不是非得從鏈中的第一個節點開始傳遞。 四:職責鏈模式使得程式中多了一些節點物件,可能在某一次的請求過程中,大部分節點並沒有起到實質性的作用,它們的作用僅僅是讓請求傳遞下去,從效能方面考慮,我們要避免過長的職責鏈帶來效能損耗。 五:如果一個節點出錯,就會導致整個鏈條斷開。

相關文章