基於策略模式的前端表單設計
Form 表單可以用來校驗使用者輸入的表單內容。通常校驗功能通常寫在元件的狀態data函式中,但是實際需求中表單驗證項一般會比較複雜,所以給每個表單項增加 validator 自定義的校驗方法就不好複用,不適合使用頻率比較高的表單驗證方法了。根據策略模式的概念和應用場景,我們可以通過結合策略模式和函式柯里化的方法很好地提高複用率和開發效率。
一、背景
策略模式(Strategy),定義了一組演算法,將每個演算法都封裝起來,並且使它們之間可以互換。關鍵是策略的實現和使用分離[1]。UML結構圖如下:
其中,Context是上下文,用一個ConcreteStrategy來配置,維護一個對Strategy物件的引用;Strategy是策略類,用於定義所有支援演算法的公共介面;ConcreteStrategy是具體策略類,封裝了具體的演算法或行為,繼承於Strategy。
當專案中需要填寫大量的表單資料,並且每個表單都要有表單驗證。一般前端採用Vue + ElementUI來實現,ElementUI 的 Form 表單具有表單驗證功能,可以用來校驗使用者輸入的表單內容。但是實際需求中表單驗證項一般會比較複雜,所以需要給每個表單項增加 validator 自定義校驗方法。
除了表單驗證,資料展示也會遇到類似的問題。介面返回的部分資料需要前端進行格式轉換後展示在頁面,例如Element 的表格控制元件的 Column 可以接受一個 formatter 引數,用來格式化內容,其型別為函式。以時間轉化為例,後端經常會直接返回時間戳,那麼前端需要根據後端的資料,根據需求轉化為自己需要的格式,如年月日/年月日時分秒等。
通常為了解決上面的兩個問題,我們可以根據官網文件把表單驗證寫在元件的data函式中,但是這樣就不好複用使用頻率比較高的表單驗證方法了,這時我們可以通過策略模式就可以很好地提高複用率和開發效率。
二、實踐
基於以上兩個問題,以及策略模式的應用場景。在專案研發中我們結合策略模式和函式柯里化的知識來重構一下。首先我們在專案的utils 資料夾實現通用的表單驗證方法:
//src/utils/validates.js /*使用者名稱校驗由2-10位漢字組成*/ Let validateUsername = function (str){ constreg=/^[\u4e00-\u9fa5]{2,10}$/returnreg.test(str) } /*郵箱校驗*/ Let validateEmail = function (str){ constreg=/^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/ returnreg.test(str) } export { validateUsername, validateEmail } export default { validateUsername, validateEmail }
然後在 utils/index.js 中增加一個柯里化方法,用來生成表單驗證函式:
//src/utils/index.js import*asValidatesfrom'./validates.js' /*生成表格自定義校驗函式*/ Export constformValidateGene=(key,msg)=>(rule,value,cb)=>{ if(Validates[key](value)){ cb() }else{ cb(newError(msg)) } }
上面的 formValidateGene 函式接受兩個引數,第一個是驗證規則,也就是 src/utils/validates.js 檔案中提取出來的通用驗證規則的方法名,第二個引數是報錯的話表單驗證的提示資訊。
<template> <el-formref="ruleForm" :rules="rules" :model="ruleForm"> <el-form-itemlabel="使用者名稱"prop="username"> <el-inputv-model="ruleForm.username"></el-input> </el-form-item> <el-form-itemlabel="郵箱"prop="email"> <el-inputv-model="ruleForm.email"></el-input> </el-form-item> …… </el-form> </template> <scripttype='text/javascript'> import*asUtilsfrom'../utils' exportdefault{ data(){ return{ ruleForm:{username:'',email:''}, rules:{ username:[{ validator:Utils.formValidateGene('validateUsername','姓名由2-10位漢字組成!'), trigger:'blur' }], email:[{ validator:Utils.formValidateGene('validateEmail','郵箱格式錯誤!'), trigger:'blur' }], …… } } } } </script>
通過上面的程式碼我們可以看到在使用的時候是非常方便的。通過把表單驗證方法提取出來作為策略,然後使用柯里化方法動態選擇表單驗證方法,從而對策略靈活運用,大大加快開發效率。
執行結果:
利用同樣的方式也可以實現表格展示的格式轉換。首先實現時間轉換通用的演算法;然後生成表格格式轉換函式,提取出來作為策略;最後直接在formatter屬性中應用即可。
模式策略把演算法的實現和使用拆分,帶來了很多優點同時也有其侷限性,並非任何情況下都合適。下面通過優缺點對比,找出合適的應用場景。
優點:
(1)策略之間相互獨立,演算法可以自由切換,提高了靈活性和複用率;
(2)避免使用多重條件判斷,增加可維護性;
(3)可擴充套件性好,策略可以很方便的進行擴充套件,增加一個策略只需實現介面即可。
缺點:
(1)策略相互獨立,因此一些複雜的演算法邏輯無法共享,造成一些資源浪費;
(2)如果使用者想採用什麼策略,必須瞭解策略的實現,因此所有策略都需向外暴露,增加了使用者對策略物件的使用成本。
通過上面的對比,常見的應用場景如下:
(1)多個演算法只在行為上稍有不同的場景,這時可以使用策略模式來動態選擇演算法;
(2)演算法需要自由切換的場景;
(3)有時需要多重條件判斷,那麼可以使用策略模式來規避多重條件判斷的情況;