JavaScript設計模式之職責鏈
阿新 • • 發佈:2018-12-18
責任鏈
什麼是責任鏈
職責鏈模式的定義是:使多個物件都有機會處理請求,從而避免請求的傳送者和接收者之間的耦合關係,將這些物件連成一條鏈,並沿著這條鏈傳遞該請求,直到有一個物件處理它為止。
- 舉個栗子:
- 當公交車很擠的時候,你從後門上車,這個時候你不可能直接把硬幣放到收款箱裡面, 因為你不知道它在哪,那你就只能把硬幣給你前面一個人,讓他幫你傳到前面一個人手上,這樣一直傳遞到站在收款箱旁邊人的手上,由他把硬幣放到收款箱裡面。
JavaScript實現職責鏈模式
Function.prototype.after = function(fn) {
var _self = this;
return function () {
// 將self的執行上下文改成window並執行函式
var ret = _self.apply(this, arguments);
// 如果函式的返回值是 nextSuccessor 則執行傳參函式
if(ret === "nextSuccessor") {
// 將fn的執行上下文改成window並執行函式
return fn.apply(this, arguments);
}
return ret;
}
}
在函式執行之後確定是否執行下一個函式,你每次呼叫after,都相當於在已有函式之後新增一個函式,至於是否執行後面這個函式,取決於前一個函式的返回值。
示例
假設這麼一個場景: 一個售賣手機的電商網站,經過分別繳納500元定金和200元定金的兩輪預定後,到了正式購買階段。針對預定使用者實行優惠,支付過500元定金的使用者會收到100元的商城優惠券,支付過200元定金的使用者會收到50元的商城優惠券,沒有支付定金的使用者歸為普通購買,且在庫存有限的情況下不一定保證買到。
傳統方式實現
/* 傳統方式實現 */
// orderType:[1:500, 2:200, 3:普通],isPaid:true/false,stock:庫存量
var order = function(orderType, isPaid, stock) {
if(orderType === 1) {
if (isPaid) {
console.log("500元定金預購,得到100優惠券");
} else {
if(stock > 0) {
console.log("普通購買,無優惠券");
}else {
console.log("庫存不足");
}
}
}else if(orderType === 2) {
if(isPaid) {
console.log("200元定金預購,得到50優惠券");
} else {
if(stock > 0) {
console.log("普通購買,無優惠券");
}else {
console.log("庫存不足");
}
}
}else if(orderType === 3) {
if(stock > 0) {
console.log("普通購買,無優惠券");
}else {
console.log("庫存不足");
}
}
}
order(1, true, 500);
可以看到耦合性相當的高,orderType === 3
條件下的程式碼在orderType === 1
和orderType === 2
條件下都有用到。而且當還有另一種又會方案的時候,需要修改order()的內容。當增加多種方案之後order()內將會有很多if
和else if
。這樣的程式碼相當難看。
責任鏈方式實現
var order500 = function(orderType, isPaid, stock) {
if(orderType === 1 && isPaid === true) {
console.log("500元定金預購,得到100優惠券");
}else {
return "nextSuccessor";
}
};
var order200 = function(orderType, isPaid, stock) {
if(orderType === 2 && isPaid === true) {
console.log("200元定金預購,得到50優惠券");
}else {
return "nextSuccessor";
}
};
// orderType === 3的情況,也是預設其他種亂輸入的情況
var orderNormal = function(orderType, isPaid, stock) {
if(stock > 0) {
console.log("普通購買,無優惠券");
}else {
console.log("庫存不足");
}
};
Function.prototype.after = function(fn) {
var self = this;
return function() {
var ret = self.apply(this, arguments);
if(ret === "nextSuccessor") {
return fn.apply(this, arguments);
}
return ret;
};
}
var order = order500.after(order200).after(orderNormal);
order(1, true, 10);
優缺點
優點
- 降低耦合度
- 動態組合職責
- 責任鏈模式會把功能處理分散到單獨的職責物件中,然後在使用的時候,就可以動態組合職形成職責鏈,從而可以靈活地給物件分配職責,也可以靈活地實現和改變物件的職責。
缺點
- 不能保證某個請求一定會被鏈中的節點處理
- 責任鏈模式的每個職責物件只負責處理自己處理的那部分,因此可能會出現某個請求,把整個責任鏈傳遞完也沒有職責物件處理它。這就需要在使用責任鏈的時候,需要提供預設的處理。
我的專案通過責任鏈修改
##沒有使用責任鏈前
if (value === 'check') {
/* 是否輸入確認密碼了 */
if (!this.checkPassword) {
this.inputErrorMessage('errorCheck', 'errorCheckText', '請確認密碼')
return false
}
/* 兩次密碼是否一致 */
if (this.password !== this.checkPassword) {
this.inputErrorMessage('errorCheck', 'errorCheckText', '兩次密碼輸入不一致')
return false
}
this.errorCheck = false
} else if (value === 'pass') {
/* 是否輸入密碼了 */
if (!this.password) {
this.inputErrorMessage('errorPass', 'errorPassText', '請輸入密碼')
return false
}
/* 密碼格式是否正確 */
let reg = /(?!^(\d+|[a-zA-Z]+|[[email protected]#$%^&*?]+)$)^[\[email protected]#$%&*?/]+$/
if (!reg.test(this.password)) {
this.inputErrorMessage('errorPass', 'errorPassText', '請輸入正確密碼格式')
return false
}
// 輸入確認密碼後又修改密碼
if (this.password !== this.checkPassword) {
if (this.checkPassword) {
this.inputErrorMessage('errorCheck', 'errorCheckText', '兩次密碼輸入不一致')
}
} else {
this.errorCheck = false
}
this.errorPass = false
}
使用責任鏈
passwordFunc (value) {
if (value === 'pass' && !this.password) {
this.inputErrorMessage('errorPass', 'errorPassText', '請輸入密碼')
} else {
return 'nextSuccessor'
}
},
passVerifyFunc (value) {
let reg = /(?!^(\d+|[a-zA-Z]+|[[email protected]#$%^&*?]+)$)^[\[email protected]#$%&*?/]+$/
if (value === 'pass' && !reg.test(this.password)) {
this.inputErrorMessage('errorPass', 'errorPassText', '請輸入正確密碼格式')
} else {
if (value === 'pass') {
this.errorPass = false
}
return 'nextSuccessor'
}
},
samePassFunc (value) {
if (this.password !== this.checkPassword && this.password && this.checkPassword) {
this.inputErrorMessage('errorCheck', 'errorCheckText', '兩次密碼輸入不一致')
} else {
return 'nextSuccessor'
}
},
checkPassFunc (value) {
if (value === 'check' && !this.checkPassword) {
this.inputErrorMessage('errorCheck', 'errorCheckText', '請確認密碼')
} else {
return 'nextSuccessor'
}
},
checkOKFunc (value) {
this.errorCheck = false
},
handlePasswordCheck (value) {
let order = this.passwordFunc.after(this.passVerifyFunc).after(this.samePassFunc).after(this.checkPassFunc).after(this.checkOKFunc)
order(value)
}
有點點複雜…不是所有的if-else都適合用這個設計模式。所以打算去學習其他的設計模式了呢~