亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何掌握JavaScript策略模式

發布時間:2021-09-30 14:39:17 來源:億速云 閱讀:120 作者:柒染 欄目:web開發

如何掌握JavaScript策略模式,針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。

JavaScript 中策略模式的使用

什么是設計模式

設想有一個電子愛好者,雖然他沒有經過正規的培訓,但是卻日積月累地設計并制造出了許多有用的電子設備:業余無線電、蓋革計數器、報警器等。有一天這個愛好者決定重新回到學校去攻讀電子學學位,來讓自己的才能得到正式的認可。隨著課程的展開,這個愛好者突然發現課程內容都似曾相識。似曾相識的不是術語或表述的方式,而是背后的概念。這個愛好者不斷學到一些名稱和原理,雖然這些名稱和原理原來他并不知道,但事實上他多年以來一直都在使用。整個過程只不過是一個接一個的頓悟。

設計模式沉思錄 ,John Vlissides, 第一章 1.2節

我們在寫代碼的時候,一定也遇到過許多類似的場景。隨著經驗的增加,我們對于這些常見場景的處理越來越得心應手,甚至總結出了針對性的“套路”,下次遇到此類問題直接運用“套路”解決,省心又省力。這些在軟件開發過程中逐漸積累下來的“套路”就是設計模式。

設計模式的目標之一就是提高代碼的可復用性、可擴展性和可維護性。正因如此,雖然有時候我們不知道某個設計模式,但是看了相關書籍或文章后會有一種“啊,原來這就是設計模式”的恍然大明白。

如果你看完這篇文章后也有此感覺,那么恭喜你,你已經在高效程序員的道路上一路狂奔了。

什么是策略模式

策略模式是一種簡單卻常用的設計模式,它的應用場景非常廣泛。我們先了解下策略模式的概念,再通過代碼示例來更清晰的認識它。

策略模式由兩部分構成:一部分是封裝不同策略的策略組,另一部分是 Context。通過組合和委托來讓 Context 擁有執行策略的能力,從而實現可復用、可擴展和可維護,并且避免大量復制粘貼的工作。

策略模式的典型應用場景是表單校驗中,對于校驗規則的封裝。接下來我們就通過一個簡單的例子具體了解一下:

粗糙的表單校驗

一個常見的登錄表單代碼如下:

<form id='login-form' action="" method="post">      <label for="account">手機號</label>      <input type="number" id="account" name="account">      <label for="password">密碼</label>      <input type="password" id="password" name="password">      <button id='login'>登錄</button>  </form>  <script>      var loginForm = document.getElementById('login-form');     loginForm.onsubmit = function (e) {         e.preventDefault();            var account = document.getElementById("account").value;          var pwd = document.getElementById("password").value;          if(account===null||account===''){              alert('手機號不能為空');              return false;          }          if(pwd===null||pwd===''){              alert('密碼不能為空');              return false;          }          if (!/(^1[3|4|5|7|8][0-9]{9}$)/.test(account)) {              alert('手機號格式錯誤');              return false;          }          if(pwd.length<6){              alert('密碼不能小于六位');              return false;          }          // ajax 發送請求      }  </script>

以上代碼,雖然功能沒問題,但是缺點也很明顯:

代碼里遍地都是 if 語句,并且它們缺乏彈性:每新增一種、或者修改原有校驗規則,我們都必須去改loginForm.onsubmit內部的代碼。另外邏輯的復用性也很差:如果有其它表單也是用同樣的規則,這段代碼并不能復用,只能復制。當校驗規則發生變化時,比如上文的正則校驗并不能匹配虛擬運營商14/17號段,我們就需要手動同步多處代碼變更(Ctrl+C/Ctrl+V)。

優秀的表單驗證

接下來我們通過策略模式的思路改寫一下上段代碼,首先抽離并封裝校驗邏輯為策略組:

var strategies = {      isNonEmpty: function (value, errorMsg) {          if (value === '' || value === null) {              return errorMsg;          }      },      isMobile: function (value, errorMsg) { // 手機號碼格式          if (!/(^1[3|4|5|7|8][0-9]{9}$)/.test(value)) {              return errorMsg;          }      },      minLength: function (value, length, errorMsg) {          if (value.length < length) {              return errorMsg;          }      }  };

接下來修改 Context:

var loginForm = document.getElementById('login-form');  loginForm.onsubmit = function (e) {      e.preventDefault();       var accountIsMobile = strategies.isMobile(account,'手機號格式錯誤');      var pwdMinLength = strategies.minLength(pwd,8,'密碼不能小于8位');      var errorMsg = accountIsMobile||pwdMinLength;       if(errorMsg){          alert(errorMsg);          return false;      } }

對比兩種實現,我們可以看到:分離了校驗邏輯的代碼如果需要擴展校驗類型,在策略組中新增定義即可使用;如果需要修改某個校驗的實現,直接修改相應策略即可全局生效。對于開發和維護都有明顯的效率提升。

擴展:史詩的表單校驗

有興趣的朋友可以了解下 async-validator ,element-ui 和 antd 的表單校驗都是基于 async-validator 封裝的,可以說是史詩級別的表單校驗了

通過表單校驗的對比,相信大家都對策略模式有所了解,那么接下來通過兩個例子具體了解下 JavaScript 中策略模式的應用:

使用策略模式調用百度AI圖像識別

因為百度AI圖像識別的接口類型不同,所需的參數格式也不盡相同。然而圖像的壓縮及上傳、錯誤處理等部分是公用的。所以可以采用策略模式封裝:

定義策略組

通過定義策略組來封裝不同的接口及其參數:比如身份證識別接口的side字段,自定義識別的templateSign字段,以及行駛證識別的接收參數為poparamstData。

/**   * 策略組   * IDCARD:身份證識別   * CUSTOMIZED:自定義識別   * VL:行駛證識別   */  var strategies = {      IDCARD: function (base64) {          return {              path: 'idcard',              param: {                  'side': 'front',                  'base64': base64              }          };      },      CUSTOMIZED: function (base64) {          return {              path: 'customized',              param: {                  'templateSign': '52cc2d402155xxxx',                  'base64': base64              }          };      },      VL: function (base64) {          return {              path: 'vehicled',              poparamstData: {                  'base64': base64              }          };      },  };

定義 Context

var ImageReader = function () { };  /**   * 讀取圖像,調用接口,獲取識別結果   *    * @param {*} type 待識別文件類型   * @param {*} base64 待識別文件 BASE64碼   * @param {*} callBack 識別結果回調   */  ImageReader.prototype.getOcrResult = function (type, base64, callBack) {      let fileSize = (base64.length / (1024 * 1024)).toFixed(2);      let compressedBase64 = '';      let image = new Image();      image.src = base64;      image.onload = function () {          /**           * 圖片壓縮處理及異常處理,代碼略           */             let postData = strategies[type](compressedBase64);         ajax(             host + postData.path, {                  data: postData.param,                  type: 'POST',                  headers: {                      'Content-Type': 'application/x-www-form-urlencoded'                  },                  success: function (res) {                      var data = JSON.parse(res);                      // 暴露給 UI 層的統一的錯誤碼                      if (data.error_code !== undefined && data.error_code !== 0) {                          var errorData = {                              error: 1,                              title: '錯誤 ' + data.error_code,                              content: 'error message'                          };                          callBack(errorData);                      } else {                          callBack(data);                      }                  }              });      };  };

調用方式

var imageReader = new ImageReader();  imageReader.getOcrResult('IDCARD', this.result.toString(), callback);

使用策略模式封裝 Vue Select 組件

某項目中多處用到了 element-ui 的 select 組件,其內在邏輯類似,都是初始化時獲取下拉列表的數據源,然后在選中某一項時 dispatch 不同的 action。遂考慮使用策略模式封裝。

Context

在本例中,組件向外部暴露一個 prop,調用方指定該 prop 從而加載不同的策略。那么定義 Context 如下:

<template>    <el-select v-model="selectedValue" placeholder="請選擇" @change="optionChanged" size="mini" clearable>      <el-option v-for="item in options" :key="item.id" :label="item.name" :value="item.id">      </el-option>    </el-select>  </template>
data() {      return {        selectedValue: undefined,        options: [],        action: "",      };    },    props: {      // 暴露給外部的 select-type      selectType: {        type: String      },    },    created() {     // 獲取 options     this.valuation();    },      methods: {      optionChanged() {        this.$emit(this.action, this.selectedValue);      },      setOptions(option) {        this.$store.dispatch(this.action, option);      },      valuation() {        // 獲取 options 數據      }    },

外部通過如下方式調用組件:

<MySelect selectType="product"/>

strategies

然后定義策略組:

let strategies = {      source: {          action: "sourceOption",          getOptions:  function() {              // 拉取 options          }      },      product: {          action: "productOption",          getOptions:  function() {              // 拉取 options          }      },      ...  }

異步

至此該組件的基本結構已經清晰,但還存在一個問題:組件加載時是異步拉取的 options, 而頁面初始化的時候很可能 options 還沒有返回,導致 select 的 options 仍為空。所以此處應該修改代碼,同步獲取 options:

// 策略組修改  source: {      action: "sourceOption",      getOptions: async function() {          // await 拉取 options      }    },  // 組件修改  methods: {      ...      async valuation() {          ...      }  }

繼續優化

但我們不是每次加載組件都需要拉取 options,如果這些 options 在其他組件或者頁面也被使用到,那么可以考慮將其存入 vuex 中。

最開始的思路是高階組件,即定義一個包裝后的select模板,通過高階組件的方式擴展其數據源與action(變化的部分)然而這個思路不是那么的vue(主要是slots不太好處理) 于是考慮策略模式改寫該組件


通過以上兩個例子,我們可以看到:

  •  策略模式符合開放-封閉原則

  •  如果代碼里需要寫大量的if-else語句,那么考慮使用策略模式

  •  如果多個組件(類)之間的區別僅在于它們的行為,考慮采用策略模式 

關于如何掌握JavaScript策略模式問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注億速云行業資訊頻道了解更多相關知識。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

新巴尔虎右旗| 合阳县| 牡丹江市| 方山县| 三门峡市| 同德县| 张家港市| 金华市| 古田县| 宝山区| 乾安县| 青龙| 泽库县| 德安县| 曲周县| 遵义县| 富锦市| 申扎县| 铁岭市| 成武县| 富宁县| 霍林郭勒市| 汉中市| 北海市| 息烽县| 伽师县| 文成县| 富川| 隆德县| 阿坝| 宾川县| 泸西县| 神木县| 东丽区| 八宿县| 安仁县| 德清县| 佛教| 贵阳市| 武义县| 北流市|