1. 程式人生 > 程式設計 >JavaScript設計模式之策略模式實現原理詳解

JavaScript設計模式之策略模式實現原理詳解

俗話說,條條大路通羅馬。在現實生活中,我們可以採用很多方法實現同一個目標。比如我們先定個小目標,先掙它一個億。我們可以根據具體的實際情況來完成這個目標。

策略模式的定義

定義一系列的演算法,把它們一個個封裝起來,並且使它們可以相互替換。

舉個例子:表單校驗

在一個Web專案中,註冊、登入等功能的實現都離不開表單提交。表單校驗也是前端常常需要做的事。假設我們正在編寫一個註冊的頁面,在點選提交按鈕之前,有如下幾條校驗邏輯:

  • 使用者名稱不可為空,不允許以空白字元命名,使用者名稱長度不能小於2位。
  • 密碼長度不能小於6位。
  • 正確的手機號碼格式。

也許,一開始我們會這麼寫:

<html>
<head>
  <title>策略模式-校驗表單</title>
  <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
</head>
<body>
  <form id = "registerForm" method="post" action="http://xxxx.com/api/register">
    使用者名稱:<input type="text" name="userName">
    密碼:<input type="text" name="password">
    手機號碼:<input type="text" name="phoneNumber">
    <button type="submit">提交</button>
  </form>
  <script type="text/javascript">
    var registerForm = document.getElementById('registerForm');
    registerForm.onsubmit = function() {
      if (registerForm.userName.value === '') {
        alert('使用者名稱不可為空');
        return false;
      }
      if (registerForm.userName.value === '') {
        alert('使用者名稱不可為空');
        return false;
      } 
      if (registerForm.userName.value.trim() === '') {
        alert('使用者名稱不允許以空白字元命名');
        return false;
      } 
      if (registerForm.userName.value.trim().length < 2) {
        alert('使用者名稱使用者名稱長度不能小於2位');
        return false;
      } 
      if (registerForm.password.value.trim().length < 6) {
        alert('密碼長度不能小於6位');
        return false;
      }
      if (!/^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|17[7]|18[0|1|2|3|5|6|7|8|9])\d{8}$/.test(registerForm.phoneNumber.value)) {
        alert('請輸入正確的手機號碼格式');
        return errorMsg;
       } 
    }
  </script>
</body>
</html>

這是一種很常見的編碼方式,但它有很明顯的缺點:

  • registerForm.onsubmit 函式比較龐大,包含了很多if語句,這些語句要覆蓋所有的校驗規則。
  • 若校驗規則有變,不得不深入到registerForm.onsubmit 函式的內部實現,違反開放-封閉原則。
  • 演算法的複用性差。

下面,讓我們來用策略模式重構表單校驗

策略模式:表單校驗

<html>
<head>
  <title>策略模式-校驗表單</title>
  <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
</head>
<body>
  <form id = "registerForm" method="post" action="http://xxxx.com/api/register">
    使用者名稱:<input type="text" name="userName">
    密碼:<input type="text" name="password">
    手機號碼:<input type="text" name="phoneNumber">
    <button type="submit">提交</button>
  </form>
  <script type="text/javascript">
    // 策略物件
    var strategies = {
      isNoEmpty: function (value,errorMsg) {
        if (value === '') {
          return errorMsg;
        }
      },isNoSpace: function (value,errorMsg) {
        if (value.trim() === '') {
          return errorMsg;
        }
      },minLength: function (value,length,errorMsg) {
        if (value.trim().length < length) {
          return errorMsg;
        }
      },maxLength: function (value,errorMsg) {
        if (value.length > length) {
          return errorMsg;
        }
      },isMobile: function (value,errorMsg) {
        if (!/^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|17[7]|18[0|1|2|3|5|6|7|8|9])\d{8}$/.test(value)) {
          return errorMsg;
        }        
      }
    }

    // 驗證類
    var Validator = function() {
      this.cache = [];
    }
    Validator.prototype.add = function(dom,rules) {
      var self = this;
      for(var i = 0,rule; rule = rules[i++];) {
        (function(rule) {
          var strategyAry = rule.strategy.split(':');
          var errorMsg = rule.errorMsg;
          self.cache.push(function() {
          var strategy = strategyAry.shift();
          strategyAry.unshift(dom.value);
          strategyAry.push(errorMsg);
          return strategies[strategy].apply(dom,strategyAry);
          })
        })(rule)
      }
    };
    Validator.prototype.start = function() {
      for(var i = 0,validatorFunc; validatorFunc = this.cache[i++];) {
        var errorMsg = validatorFunc();
        if (errorMsg) {
          return errorMsg;
        }
      }
    };

    // 呼叫程式碼
    var registerForm = document.getElementById('registerForm');

    var validataFunc = function() {
      var validator = new Validator();
      validator.add(registerForm.userName,[{
        strategy: 'isNoEmpty',errorMsg: '使用者名稱不可為空'
      },{
        strategy: 'isNoSpace',errorMsg: '不允許以空白字元命名'
      },{
        strategy: 'minLength:2',errorMsg: '使用者名稱長度不能小於2位'
      }]);
      validator.add(registerForm.password,[ {
        strategy: 'minLength:6',errorMsg: '密碼長度不能小於6位'
      }]);
      validator.add(registerForm.phoneNumber,[{
        strategy: 'isMobile',errorMsg: '請輸入正確的手機號碼格式'
      }]);
      var errorMsg = validator.start();
      return errorMsg;
    }

    registerForm.onsubmit = function() {
      var errorMsg = validataFunc();
      if (errorMsg) {
        alert(errorMsg);
        return false;
      }
    }
  </script>
</body>
</html>

策略模式優缺點

策略模式是一種常用且有效的設計模式。

  • 策略模式可以有效避免多重條件選擇語句。
  • 策略模式提供了對開放-封裝原則的完美支援,將方法封裝在獨立的strategy中,使得它們易於切換,易於理解,易於擴充套件。
  • 複用性高。

當然,策略模式也有一些缺點

  • 增加了許多策略類或者策略物件。
  • 要使用策略模式,必須瞭解所有的strategy,違反了最少知識原則。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。