這裡有一份簡潔的前端知識體系等待你查收,看看吧,會有驚喜哦~如果覺得不錯,懇求star哈~
職責鏈模式簡述
職責連是由多個不同的物件組成的,有傳送者跟接收者,分別負責資訊的傳送跟接收,其中,鏈中第一個物件是 職責連是由多個不同的物件組成的,傳送者是傳送請求的物件,接收者接收請求並且對其進行處理或傳遞的物件。基本流程如下:
- 傳送者知道鏈中的第一個接收者,它向這個接收者傳送該請求。
- 每一個接收者都對請求進行分析,然後要麼處理它,要麼它往下傳遞。
- 每一個接收者知道其他的物件只有一個,即它在鏈中的下家(successor)。
- 如果沒有任何接收者處理請求,那麼請求會從鏈中離開。
職責鏈模式是個鏈式結構,請求在鏈中的節點之間依次傳遞,直到有一個物件能處理該請求為止。如果沒有任何物件處理該請求的話,那麼請求就會從鏈中離開。
實戰
以電商網站抽獎為例,規則如下:
- 使用者充值500元,可以100%中獎100元紅包
- 使用者充值200元,可以100%中獎20元紅包
- 使用者不充值,也可以抽獎,但概率極低。
- 使用者充值失敗,按不充值處理。
常規實現
首先需要定義幾個欄位:
- orderType:充值型別。如果值為1的話,說明是充值500元的使用者,如果為2的話,說明是充值200元的使用者,如果是3的話,說明是沒有充值的使用者。
- isPay:是否已經成功充值了。 如果該值為true的話,說明已經成功充值了,否則的話說明沒有充值成功;就當作普通使用者來購買。
- count:表示數量。普通使用者抽獎,如果數量有的話,就可以拿到優惠卷,否則的話,不能拿到優惠卷。
實現如下:
var order = function(orderType,isPay,count) {
if(orderType == 1) { // 使用者充值500元
if(isPay == true) { // 如果充值成功的話,100%中獎
console.log("親愛的使用者,您中獎了100元紅包了");
}else {
// 充值失敗,就當作普通使用者來處理中獎資訊
if(count > 0) {
console.log("親愛的使用者,您已抽到10元優惠卷");
}else {
console.log("親愛的使用者,請再接再厲哦");
}
}
}else if(orderType == 2) { // 使用者充值200元
if(isPay == true) { // 如果充值成功的話,100%中獎
console.log("親愛的使用者,您中獎了20元紅包了");
}else {
// 充值失敗,就當作普通使用者來處理中獎資訊
if(count > 0) {
console.log("親愛的使用者,您已抽到10元優惠卷");
}else {
console.log("親愛的使用者,請再接再厲哦");
}
}
}else if(orderType == 3) {
// 普通使用者來處理中獎資訊
if(count > 0) {
console.log("親愛的使用者,您已抽到10元優惠卷");
}else {
console.log("親愛的使用者,請再接再厲哦");
}
}
};
複製程式碼
如上程式碼,雖然實現了需求,但存在的問題也比較突出: 一、業務邏輯程式碼耦合度太高,如果想增加條件,比如充值300可以中獎150元紅包,這時候就很容易改出問題 二、冗餘程式碼太多,普通使用者抽獎的程式碼是可以單獨抽離出來的
職責鏈實現
function order500(orderType,isPay,count){
if(orderType == 1 && isPay == true) {
console.log("親愛的使用者,您中獎了100元紅包了");
}else {
//我不知道下一個節點是誰,反正把請求往後面傳遞
return "nextSuccessor";
}
};
function order200(orderType,isPay,count) {
if(orderType == 2 && isPay == true) {
console.log("親愛的使用者,您中獎了20元紅包了");
}else {
//我不知道下一個節點是誰,反正把請求往後面傳遞
return "nextSuccessor";
}
};
function orderNormal(orderType,isPay,count){
// 普通使用者來處理中獎資訊
if(count > 0) {
console.log("親愛的使用者,您已抽到10元優惠卷");
}else {
console.log("親愛的使用者,請再接再厲哦");
}
}
// 下面需要編寫職責鏈模式的封裝建構函式方法
var Chain = function(fn){
this.fn = fn;
this.successor = null;
};
Chain.prototype.setNextSuccessor = function(successor){
return this.successor = successor;
}
// 把請求往下傳遞
Chain.prototype.passRequest = function(){
var ret = this.fn.apply(this,arguments);
if(ret === 'nextSuccessor') {
return this.successor && this.successor.passRequest.apply(this.successor,arguments);
}
return ret;
}
//現在我們把3個函式分別包裝成職責鏈節點:
var chainOrder500 = new Chain(order500);
var chainOrder200 = new Chain(order200);
var chainOrderNormal = new Chain(orderNormal);
// 然後指定節點在職責鏈中的順序
chainOrder500.setNextSuccessor(chainOrder200);
chainOrder200.setNextSuccessor(chainOrderNormal);
//最後把請求傳遞給第一個節點:
chainOrder500.passRequest(1,true,500); // 親愛的使用者,您中獎了100元紅包了
chainOrder500.passRequest(2,true,500); // 親愛的使用者,您中獎了20元紅包了
chainOrder500.passRequest(3,true,500); // 親愛的使用者,您已抽到10元優惠卷
chainOrder500.passRequest(1,false,0); // 親愛的使用者,請再接再厲哦
複製程式碼
如上程式碼: 一、分別編寫order500,order200,orderNormal三個函式,處理自己的業務邏輯,如果自己的函式不能處理的話,就返回字串nextSuccessor 往後面傳遞 二、封裝Chain建構函式,接收fn做為引數,且帶有屬性successor。例項化後的Chain型別的物件,就像一個一個獨立存在的節點。 三、Chain建構函式原型上有2個方法,分別是setNextSuccessor 和 passRequest。setNextSuccessor 方法指定了節點在職責鏈中的順序,passRequest方法是將請求轉移到職責鏈的下一個節點。
如上文提到的,假設需要實現“充值300中獎150元”,我們可以編寫order300這個函式,通過Chain包裝起來,指定在職責鏈中的順序即可。業務邏輯的程式碼不需要任何處理。
現在又有一個問題,我們在開發中經常會碰到ajax非同步請求,如果我們用上面的做法,就不生效了。由此,便引出了非同步的職責鏈來解決這個問題。我們給Chain類再增加一個原型方法Chain.prototype.next,表示手動傳遞請求給職責鏈中的一下個節點。
function Fn1() {
console.log(1);
return "nextSuccessor";
}
function Fn2() {
console.log(2);
var self = this;
setTimeout(function(){
self.next();
},1000);
}
function Fn3() {
console.log(3);
}
// 下面需要編寫職責鏈模式的封裝建構函式方法
var Chain = function(fn){
this.fn = fn;
this.successor = null;
};
Chain.prototype.setNextSuccessor = function(successor){
return this.successor = successor;
}
// 把請求往下傳遞
Chain.prototype.passRequest = function(){
var ret = this.fn.apply(this,arguments);
if(ret === 'nextSuccessor') {
return this.successor && this.successor.passRequest.apply(this.successor,arguments);
}
return ret;
}
Chain.prototype.next = function(){
return this.successor && this.successor.passRequest.apply(this.successor,arguments);
}
//現在我們把3個函式分別包裝成職責鏈節點:
var chainFn1 = new Chain(Fn1);
var chainFn2 = new Chain(Fn2);
var chainFn3 = new Chain(Fn3);
// 然後指定節點在職責鏈中的順序
chainFn1.setNextSuccessor(chainFn2);
chainFn2.setNextSuccessor(chainFn3);
chainFn1.passRequest(); // 列印出1,2 過1秒後 會列印出3
複製程式碼
如上程式碼,當執行到F2時,遇到了定時器非同步函式,當定時器執行結束後,呼叫了next方法,手動將請求交給職責鏈中的下一個節點,因此過了一秒後,會列印3。
總結
優點
- 解耦了請求傳送者和度個接收者之間的複雜關係,不需要知道鏈中哪個節點能處理你的請求,只需要把請求傳遞到第一個節點即可。
- 鏈中的節點物件可以靈活地拆分重組,增加或刪除一個節點,或者改變節點的位置都是很簡單的事情。
- 我們還可以手動指定節點的起始位置,並不是說非得要從其實節點開始傳遞的.
缺點
職責鏈模式中多了一點節點物件,可能在某一次請求過程中,大部分節點沒有起到實質性作用,他們的作用只是讓請求傳遞下去,從效能方面考慮,避免過長的職責鏈提高效能。