国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

5分鐘即可掌握的前端高效利器:JavaScript策略模式

開(kāi)發(fā) 前端
策略模式是一種簡(jiǎn)單卻常用的設(shè)計(jì)模式,它的應(yīng)用場(chǎng)景非常廣泛。我們先了解下策略模式的概念,再通過(guò)代碼示例來(lái)更清晰的認(rèn)識(shí)它。

 [[341697]]

淺談 JavaScript 中策略模式的使用:

  •  什么是設(shè)計(jì)模式
  •  什么是策略模式
  •  策略模式在 JavaScript 中的應(yīng)用(使用策略模式封裝百度AI識(shí)別調(diào)用)
  •  策略模式在 Vue 組件封裝中的應(yīng)用(使用策略模式封裝Select組件)

什么是設(shè)計(jì)模式

設(shè)想有一個(gè)電子愛(ài)好者,雖然他沒(méi)有經(jīng)過(guò)正規(guī)的培訓(xùn),但是卻日積月累地設(shè)計(jì)并制造出了許多有用的電子設(shè)備:業(yè)余無(wú)線電、蓋革計(jì)數(shù)器、報(bào)警器等。有一天這個(gè)愛(ài)好者決定重新回到學(xué)校去攻讀電子學(xué)學(xué)位,來(lái)讓自己的才能得到正式的認(rèn)可。隨著課程的展開(kāi),這個(gè)愛(ài)好者突然發(fā)現(xiàn)課程內(nèi)容都似曾相識(shí)。似曾相識(shí)的不是術(shù)語(yǔ)或表述的方式,而是背后的概念。這個(gè)愛(ài)好者不斷學(xué)到一些名稱(chēng)和原理,雖然這些名稱(chēng)和原理原來(lái)他并不知道,但事實(shí)上他多年以來(lái)一直都在使用。整個(gè)過(guò)程只不過(guò)是一個(gè)接一個(gè)的頓悟。

設(shè)計(jì)模式沉思錄 ,John Vlissides, 第一章 1.2節(jié)

我們?cè)趯?xiě)代碼的時(shí)候,一定也遇到過(guò)許多類(lèi)似的場(chǎng)景。隨著經(jīng)驗(yàn)的增加,我們對(duì)于這些常見(jiàn)場(chǎng)景的處理越來(lái)越得心應(yīng)手,甚至總結(jié)出了針對(duì)性的“套路”,下次遇到此類(lèi)問(wèn)題直接運(yùn)用“套路”解決,省心又省力。這些在軟件開(kāi)發(fā)過(guò)程中逐漸積累下來(lái)的“套路”就是設(shè)計(jì)模式。

設(shè)計(jì)模式的目標(biāo)之一就是提高代碼的可復(fù)用性、可擴(kuò)展性和可維護(hù)性。正因如此,雖然有時(shí)候我們不知道某個(gè)設(shè)計(jì)模式,但是看了相關(guān)書(shū)籍或文章后會(huì)有一種“啊,原來(lái)這就是設(shè)計(jì)模式”的恍然大明白。

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

什么是策略模式

策略模式是一種簡(jiǎn)單卻常用的設(shè)計(jì)模式,它的應(yīng)用場(chǎng)景非常廣泛。我們先了解下策略模式的概念,再通過(guò)代碼示例來(lái)更清晰的認(rèn)識(shí)它。

策略模式由兩部分構(gòu)成:一部分是封裝不同策略的策略組,另一部分是 Context。通過(guò)組合和委托來(lái)讓 Context 擁有執(zhí)行策略的能力,從而實(shí)現(xiàn)可復(fù)用、可擴(kuò)展和可維護(hù),并且避免大量復(fù)制粘貼的工作。

策略模式的典型應(yīng)用場(chǎng)景是表單校驗(yàn)中,對(duì)于校驗(yàn)規(guī)則的封裝。接下來(lái)我們就通過(guò)一個(gè)簡(jiǎn)單的例子具體了解一下:

粗糙的表單校驗(yàn)

一個(gè)常見(jiàn)的登錄表單代碼如下: 

  1. <form id='login-form' action="" method="post">  
  2.     <label for="account">手機(jī)號(hào)</label>  
  3.     <input type="number" id="account" name="account">  
  4.     <label for="password">密碼</label>  
  5.     <input type="password" id="password" name="password">  
  6.     <button id='login'>登錄</button>  
  7. </form>  
  8. <script> 
  9.      var loginForm = document.getElementById('login-form'); 
  10.     loginForm.onsubmit = function (e) { 
  11.         e.preventDefault();    
  12.         var account = document.getElementById("account").value;  
  13.         var pwd = document.getElementById("password").value;  
  14.         if(account===null||account===''){  
  15.             alert('手機(jī)號(hào)不能為空');  
  16.             return false;  
  17.         }  
  18.         if(pwd===null||pwd===''){  
  19.             alert('密碼不能為空');  
  20.             return false;  
  21.         }  
  22.         if (!/(^1[3|4|5|7|8][0-9]{9}$)/.test(account)) {  
  23.             alert('手機(jī)號(hào)格式錯(cuò)誤');  
  24.             return false;  
  25.         }  
  26.         if(pwd.length<6){  
  27.             alert('密碼不能小于六位');  
  28.             return false;  
  29.         }  
  30.         // ajax 發(fā)送請(qǐng)求  
  31.     }  
  32. </script> 

以上代碼,雖然功能沒(méi)問(wèn)題,但是缺點(diǎn)也很明顯:

代碼里遍地都是 if 語(yǔ)句,并且它們?nèi)狈椥裕好啃略鲆环N、或者修改原有校驗(yàn)規(guī)則,我們都必須去改loginForm.onsubmit內(nèi)部的代碼。另外邏輯的復(fù)用性也很差:如果有其它表單也是用同樣的規(guī)則,這段代碼并不能復(fù)用,只能復(fù)制。當(dāng)校驗(yàn)規(guī)則發(fā)生變化時(shí),比如上文的正則校驗(yàn)并不能匹配虛擬運(yùn)營(yíng)商14/17號(hào)段,我們就需要手動(dòng)同步多處代碼變更(Ctrl+C/Ctrl+V)。

優(yōu)秀的表單驗(yàn)證

接下來(lái)我們通過(guò)策略模式的思路改寫(xiě)一下上段代碼,首先抽離并封裝校驗(yàn)邏輯為策略組: 

  1. var strategies = {  
  2.     isNonEmpty: function (value, errorMsg) {  
  3.         if (value === '' || value === null) {  
  4.             return errorMsg;  
  5.         }  
  6.     },  
  7.     isMobile: function (value, errorMsg) { // 手機(jī)號(hào)碼格式  
  8.         if (!/(^1[3|4|5|7|8][0-9]{9}$)/.test(value)) {  
  9.             return errorMsg;  
  10.         }  
  11.     },  
  12.     minLength: function (value, length, errorMsg) {  
  13.         if (value.length < length) {  
  14.             return errorMsg;  
  15.         }  
  16.     }  
  17. }; 

接下來(lái)修改 Context: 

  1. var loginForm = document.getElementById('login-form');  
  2. loginForm.onsubmit = function (e) {  
  3.     e.preventDefault();   
  4.     var accountIsMobile = strategies.isMobile(account,'手機(jī)號(hào)格式錯(cuò)誤');  
  5.     var pwdMinLength = strategies.minLength(pwd,8,'密碼不能小于8位');  
  6.     var errorMsg = accountIsMobile||pwdMinLength;   
  7.     if(errorMsg){  
  8.         alert(errorMsg); 
  9.          return false;  
  10.     } 

對(duì)比兩種實(shí)現(xiàn),我們可以看到:分離了校驗(yàn)邏輯的代碼如果需要擴(kuò)展校驗(yàn)類(lèi)型,在策略組中新增定義即可使用;如果需要修改某個(gè)校驗(yàn)的實(shí)現(xiàn),直接修改相應(yīng)策略即可全局生效。對(duì)于開(kāi)發(fā)和維護(hù)都有明顯的效率提升。

擴(kuò)展:史詩(shī)的表單校驗(yàn)

有興趣的朋友可以了解下 async-validator ,element-ui 和 antd 的表單校驗(yàn)都是基于 async-validator 封裝的,可以說(shuō)是史詩(shī)級(jí)別的表單校驗(yàn)了

通過(guò)表單校驗(yàn)的對(duì)比,相信大家都對(duì)策略模式有所了解,那么接下來(lái)通過(guò)兩個(gè)例子具體了解下 JavaScript 中策略模式的應(yīng)用:

使用策略模式調(diào)用百度AI圖像識(shí)別

因?yàn)榘俣華I圖像識(shí)別的接口類(lèi)型不同,所需的參數(shù)格式也不盡相同。然而圖像的壓縮及上傳、錯(cuò)誤處理等部分是公用的。所以可以采用策略模式封裝:

定義策略組

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

  1. /**  
  2.  * 策略組  
  3.  * IDCARD:身份證識(shí)別  
  4.  * CUSTOMIZED:自定義識(shí)別  
  5.  * VL:行駛證識(shí)別  
  6.  */  
  7. var strategies = {  
  8.     IDCARD: function (base64) {  
  9.         return {  
  10.             path: 'idcard',  
  11.             param: {  
  12.                 'side': 'front',  
  13.                 'base64': base64  
  14.             }  
  15.         };  
  16.     },  
  17.     CUSTOMIZED: function (base64) {  
  18.         return {  
  19.             path: 'customized',  
  20.             param: {  
  21.                 'templateSign': '52cc2d402155xxxx',  
  22.                 'base64': base64  
  23.             }  
  24.         };  
  25.     },  
  26.     VL: function (base64) {  
  27.         return {  
  28.             path: 'vehicled',  
  29.             poparamstData: {  
  30.                 'base64': base64  
  31.             }  
  32.         };  
  33.     },  
  34. }; 

定義 Context 

  1. var ImageReader = function () { };  
  2. /**  
  3.  * 讀取圖像,調(diào)用接口,獲取識(shí)別結(jié)果  
  4.  *   
  5.  * @param {*} type 待識(shí)別文件類(lèi)型  
  6.  * @param {*} base64 待識(shí)別文件 BASE64碼  
  7.  * @param {*} callBack 識(shí)別結(jié)果回調(diào)  
  8.  */  
  9. ImageReader.prototype.getOcrResult = function (type, base64, callBack) {  
  10.     let fileSize = (base64.length / (1024 * 1024)).toFixed(2);  
  11.     let compressedBase64 = '' 
  12.     let image = new Image();  
  13.     image.src = base64 
  14.     image.onload = function () {  
  15.         /**  
  16.          * 圖片壓縮處理及異常處理,代碼略  
  17.          */     
  18.         let postData = strategies[type](compressedBase64); 
  19.         ajax( 
  20.             host + postData.path, {  
  21.                 data: postData.param,  
  22.                 type: 'POST',  
  23.                 headers: {  
  24.                     'Content-Type': 'application/x-www-form-urlencoded'  
  25.                 },  
  26.                 success: function (res) {  
  27.                     var data = JSON.parse(res);  
  28.                     // 暴露給 UI 層的統(tǒng)一的錯(cuò)誤碼  
  29.                     if (data.error_code !== undefined && data.error_code !== 0) {  
  30.                         var errorData = {  
  31.                             error: 1,  
  32.                             title: '錯(cuò)誤 ' + data.error_code,  
  33.                             content: 'error message'  
  34.                         };  
  35.                         callBack(errorData);  
  36.                     } else {  
  37.                         callBack(data);  
  38.                     }  
  39.                 }  
  40.             });  
  41.     };  
  42. }; 

調(diào)用方式 

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

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

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

Context

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

  1. <template>  
  2.   <el-select v-model="selectedValue" placeholder="請(qǐng)選擇" @change="optionChanged" size="mini" clearable>  
  3.     <el-option v-for="item in options" :key="item.id" :label="item.name" :value="item.id">  
  4.     </el-option>  
  5.   </el-select>  
  6. </template>  
  1. data() {  
  2.     return {  
  3.       selectedValue: undefined,  
  4.       options: [],  
  5.       action: "",  
  6.     };  
  7.   },  
  8.   props: {  
  9.     // 暴露給外部的 select-type  
  10.     selectType: {  
  11.       type: String  
  12.     },  
  13.   },  
  14.   created() {  
  15.    // 獲取 options  
  16.    this.valuation();  
  17.   },  
  18.     methods: {  
  19.     optionChanged() {  
  20.       this.$emit(this.action, this.selectedValue);  
  21.     },  
  22.     setOptions(option) {  
  23.       this.$store.dispatch(this.action, option);  
  24.     },  
  25.     valuation() {  
  26.       // 獲取 options 數(shù)據(jù)  
  27.     }  
  28.   }, 

外部通過(guò)如下方式調(diào)用組件:

  1. <MySelect selectType="product"/> 

strategies

然后定義策略組: 

  1. let strategies = {  
  2.     source: {  
  3.         action: "sourceOption",  
  4.         getOptions:  function() {  
  5.             // 拉取 options  
  6.         }  
  7.     },  
  8.     product: {  
  9.         action: "productOption",  
  10.         getOptions:  function() {  
  11.             // 拉取 options  
  12.         }  
  13.     },  
  14.     ...  

異步

至此該組件的基本結(jié)構(gòu)已經(jīng)清晰,但還存在一個(gè)問(wèn)題:組件加載時(shí)是異步拉取的 options, 而頁(yè)面初始化的時(shí)候很可能 options 還沒(méi)有返回,導(dǎo)致 select 的 options 仍為空。所以此處應(yīng)該修改代碼,同步獲取 options: 

  1. // 策略組修改  
  2. source: {  
  3.     action: "sourceOption",  
  4.     getOptions: async function() {  
  5.         // await 拉取 options  
  6.     }  
  7.   },  
  8. // 組件修改  
  9. methods: {  
  10.     ...  
  11.     async valuation() {  
  12.         ...  
  13.     }  

繼續(xù)優(yōu)化

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

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

總結(jié)

通過(guò)以上兩個(gè)例子,我們可以看到:

  •  策略模式符合開(kāi)放-封閉原則
  •  如果代碼里需要寫(xiě)大量的if-else語(yǔ)句,那么考慮使用策略模式
  •  如果多個(gè)組件(類(lèi))之間的區(qū)別僅在于它們的行為,考慮采用策略模式 

 

責(zé)任編輯:龐桂玉 來(lái)源: segmentfault
相關(guān)推薦

2020-05-15 07:30:08

黑客Thunderbolt漏洞

2021-06-07 09:51:22

原型模式序列化

2020-12-17 10:00:16

Python協(xié)程線程

2021-03-12 09:45:00

Python關(guān)聯(lián)規(guī)則算法

2021-01-29 11:25:57

Python爬山算法函數(shù)優(yōu)化

2020-12-07 11:23:32

Scrapy爬蟲(chóng)Python

2021-03-23 15:35:36

Adam優(yōu)化語(yǔ)言

2019-07-24 15:29:55

JavaScript開(kāi)發(fā) 技巧

2017-01-10 09:07:53

tcpdumpGET請(qǐng)求

2019-12-23 16:42:44

JavaScript前端開(kāi)發(fā)

2020-10-27 10:43:24

Redis字符串數(shù)據(jù)庫(kù)

2018-01-30 05:04:06

2020-12-01 12:44:44

PythonHook鉤子函數(shù)

2020-11-24 11:50:52

Python文件代碼

2009-11-17 14:50:50

Oracle調(diào)優(yōu)

2021-04-19 23:29:44

MakefilemacOSLinux

2021-01-11 09:33:37

Maven數(shù)目項(xiàng)目

2025-01-20 08:50:00

2020-11-10 16:01:25

程序員設(shè)計(jì)模式技術(shù)

2012-06-28 10:26:51

Silverlight
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

久久久久久一区二区| 欧美猛交免费看| 国产成人精品电影| 好吊日av在线| 一区二区高清| 精品国产网站地址| 在线看av的网址| 国产蜜臀av在线一区二区三区| 欧美日韩免费高清| 欧美激情偷拍自拍| 欧美亚洲日本网站| 亚洲一区二区小说| 亚洲成人精品在线| 亚洲h片在线看| 欧美在线一区二区| 欧洲伦理片一区 二区 三区| 欧美激情中文字幕一区二区| 人妻少妇精品久久| 狂野欧美性猛交xxxx巴西| 97人人爽人人喊人人模波多 | 九九在线精品视频| 久久久久久久久久久久久久一区| 激情欧美日韩一区二区| 涩涩视频网站在线观看| 91麻豆精品国产91久久久久 | 久久久久久久久电影| 日韩精品视频一区二区三区| 久久精品久久久久久| 日韩激情精品| 国产成+人+综合+亚洲欧洲| 日韩在线综合| 国产精品视频免费观看www| 国产欧美日韩精品一区| 久久五月天婷婷| 狠狠干综合网| 在线视频精品一| 98在线视频| 最新热久久免费视频| 国产精品乱码| 国产高清不卡| 亚洲成人av电影在线| 国产九色精品| 国产大片一区| 日韩中文字幕在线播放| 麻豆免费在线| 欧美在线日韩| y111111国产精品久久婷婷| 在线视频精品| 日韩av片网站| 欧美精品视频www在线观看| 成人在线影视| 亚洲色图在线观看| 媚黑女一区二区| 在线观看成人黄色| 91看片在线观看| 亚洲欧美日韩一区在线| 激情亚洲网站| 好男人社区在线视频| 欧美一级精品大片| 99国产精品久久一区二区三区| 国产日韩精品一区观看| 黄色一级片在线看| 国产精品嫩草影院com| 波多野结衣精品| 91久久精品国产| 国产一区二区三区免费观看 | 中日韩免视频上线全都免费| 九九久久久久久久久激情| 欧美日韩国产在线一区| 国产精品igao| 99久久亚洲精品| 黄色免费网址大全| 久久91精品国产| 波多野结衣91| av资源中文在线| 国产乱码精品一区二区三区中文| 91福利国产精品| 国产综合av| 国产免费一区二区三区| 污污的网站在线观看| **网站欧美大片在线观看| 我要看一级黄色大片| 亚洲一二三四区| 国产白浆在线免费观看| 日韩欧美精品一区二区| 这里只有精品免费| 国产麻豆一精品一av一免费| 五月天激情在线| 水蜜桃色314在线观看| 999精品色在线播放| 白天操夜夜操| 欧美大荫蒂xxx| 亚洲va国产va欧美va观看| 国产精品入口| 成人自拍视频| 最大av网站| 国产精品一区二区三区四区五区 | 日韩国产欧美三级| av免费网站观看| 欧美伦理影院| 一区二区三区四区久久| 久久动漫亚洲| 无码免费一区二区三区免费播放| 亚洲视频在线一区| 自拍视频在线看| 成人豆花视频| 国产在线高清理伦片a| 91九色精品视频| 亚洲男人第一av网站| 国产资源在线一区| 动漫精品视频| 亚洲精品日产精品乱码不卡| 国产精品专区免费| 一区二区亚洲精品国产| 国产精品蜜臀在线观看| 国产精品久一| 台湾av在线二三区观看| 午夜精品在线视频| 51xx午夜影福利| 久久老女人爱爱| 在线女人免费视频| 欧美精品久久久久久久| 欧美一区二区美女| av不卡一区二区三区| 17videosex性欧美| 国产真实老熟女无套内射| 亚洲影院一区| 色播在线观看| 欧美一区二区三区四区在线| 久久久精品国产99久久精品芒果| 国产精品免费观看在线| 欧美无砖专区一中文字| 欧美激情一级欧美精品| 粉嫩13p一区二区三区| caoporn视频在线| 亚洲欧洲日韩综合二区| 日韩你懂的在线观看| 美腿丝袜在线亚洲一区| 亚洲福利影院| 国产精品无码av在线播放| 九九久久精品一区| 国产精品无码永久免费888| 视频福利一区| 日本一二三区在线视频| 国产欧美日韩在线播放| 日韩欧美国产一二三区| 国产精品主播直播| 91亚洲无吗| 性生大片免费观看性| heyzo欧美激情| 91吃瓜在线观看| 国产精品theporn88| 成人欧美一区二区三区白人 | 99久热re在线精品996热视频| 91成人在线精品| 99国内精品| 希岛爱理一区二区三区av高清| 日韩精品在线中文字幕| 97国产真实伦对白精彩视频8| 一区二区三区日韩精品视频| 欧美日韩影院| 超碰高清在线| 色一情一乱一伦一区二区三区日本| 97成人在线视频| 在线免费不卡视频| 麻豆一区二区三| 无码国模国产在线观看| 偷拍自拍在线| 国产又大又长又粗又黄| 国内精品美女av在线播放| 91高清在线观看| 亚洲mv在线| 最近中文字幕免费mv| 久久综合一区二区| 高清一区二区三区| 国产在线中文字幕| 东北少妇不带套对白| 国产欧美久久久久久| 亚洲国产精品va在线看黑人动漫| 26uuu国产在线精品一区二区| 日韩欧美一区二区三区免费看| 男人天堂亚洲天堂| 色偷偷亚洲女人天堂观看欧| 国产精品一区二区免费看| 日韩av在线免费| 亚洲一区在线免费观看| 久久99国产精品久久99果冻传媒| 理论片一区二区在线| av免费在线免费| 99爱免费视频| 亚洲欧洲久久| 国产精品嫩草影院一区二区| 亚洲日本在线观看视频| 亚洲网一区二区三区| 丁香婷婷久久| 这里只有精品在线观看| 日韩欧美在线视频| 91色.com| 久久精品久久综合| 日韩免费一区二区三区|