1. 程式人生 > >JavaScript設計模式之職責鏈

JavaScript設計模式之職責鏈

責任鏈

什麼是責任鏈

職責鏈模式的定義是:使多個物件都有機會處理請求,從而避免請求的傳送者和接收者之間的耦合關係,將這些物件連成一條鏈,並沿著這條鏈傳遞該請求,直到有一個物件處理它為止。

  • 舉個栗子:
    • 當公交車很擠的時候,你從後門上車,這個時候你不可能直接把硬幣放到收款箱裡面, 因為你不知道它在哪,那你就只能把硬幣給你前面一個人,讓他幫你傳到前面一個人手上,這樣一直傳遞到站在收款箱旁邊人的手上,由他把硬幣放到收款箱裡面。

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 === 1orderType === 2條件下都有用到。而且當還有另一種又會方案的時候,需要修改order()的內容。當增加多種方案之後order()內將會有很多ifelse 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都適合用這個設計模式。所以打算去學習其他的設計模式了呢~

文章有引用一篇我之前看過的但是忘了地址的文章了,不過這只是我的筆記啦~~