1.前言
上篇文章,主要介紹了重構(gòu)的一些概念和一些簡單的實例。這一次,詳細(xì)的說下項目中的一個重構(gòu)場景--給API設(shè)計擴(kuò)展機(jī)制。目的就是為了方便以后能靈活應(yīng)對需求的改變。當(dāng)然了,是否需要設(shè)計擴(kuò)展性這個要看API的需求。如果大家有什么建議,歡迎評論留言。
2.擴(kuò)展性表現(xiàn)形式 2-1.prototype這個可以說是JS里面最原的一個擴(kuò)展。比如原生JS沒有提供打亂數(shù)組順序的API,但是開發(fā)者又想方便使用,這樣的話,就只能擴(kuò)展數(shù)組的prototype。代碼如下
//擴(kuò)展Array.prototype,增加打亂數(shù)組的方法。 Array.prototype.upset=function(){ return this.sort((n1,n2)=>Math.random() - 0.5); } let arr=[1,2,3,4,5]; //調(diào)用 arr.upset(); //顯示結(jié)果 console.log(arr);
運行結(jié)果
功能是實現(xiàn)了。但是上面的代碼,只想借用例子講解擴(kuò)展性,大家看下就好。不要模仿,也不要在項目這樣寫。現(xiàn)在基本都禁止這樣開發(fā)了。理由也很簡單,之前的文章也有提到過。這里重復(fù)一下。
這樣就污染了原生對象Array,別人創(chuàng)建的Array也會被污染,造成不必要的開銷。最可怕的是,萬一自己命名的跟原生的方法重名了,就被覆蓋原來的方法了。
Array.prototype.push=function(){console.log("守候")} let arrTest=[123] arrTest.push() //result:守候 //push方法有什么作用,大家應(yīng)該知道,不知道的可以去w3c看下2-2.jQuery
關(guān)于 jQuery 的擴(kuò)展性,分別提供了三個API:$.extend()、$.fn和$.fn.extend()。分別對jQuery的本身,靜態(tài)方法,原型對象進(jìn)行擴(kuò)展,基于jQuery寫插件的時候,最離不開的應(yīng)該就是$.fn.extend()。
參考鏈接:
理解jquery的$.extend()、$.fn和$.fn.extend()
Jquery自定義插件之$.extend()、$.fn和$.fn.extend()
對VUE進(jìn)行擴(kuò)展,引用官網(wǎng)(插件)的說法,擴(kuò)展的方式一般有以下幾種:
1.添加全局方法或者屬性,如: vue-custom-element
2.添加全局資源:指令/過濾器/過渡等,如 vue-touch
3.通過全局 mixin 方法添加一些組件選項,如: vue-router
4.添加 Vue 實例方法,通過把它們添加到 Vue.prototype 上實現(xiàn)。
5.一個庫,提供自己的 API,同時提供上面提到的一個或多個功能,如 vue-router
基于VUE的擴(kuò)展。在組件,插件的內(nèi)容提供一個install方法。如下
使用組件
上面幾個擴(kuò)展性的實例分別是原生對象,庫,框架的擴(kuò)展,大家可能覺得有點夸夸而談,那下面就分享一個日常開發(fā)常用的一個實例。3.實例-表單驗證
看了上面那些擴(kuò)展性的實例,下面看下一個在日常開發(fā)使用得也很多的一個實例:表單驗證。這塊可以說很簡單,但是做好,做通用不簡單。看了《JavaScript設(shè)計模式與開發(fā)實踐》,用策略模式對以前的表單驗證函數(shù)進(jìn)行了一個重構(gòu)。下面進(jìn)行一個簡單的分析。
下面的內(nèi)容,代碼會偏多,雖然代碼不難,但還是強(qiáng)烈建議大家不要只看,要邊看,邊寫,邊調(diào)試,不然作為讀者,很可能不知道我的代碼是什么意思,很容易懵。下面的代碼回涉兩個知識:開放-封閉原則和策略模式,大家可以自行了解。3-1.原來方案
/** * @description 字段檢驗 * @param checkArr * @returns {boolean} */ function validateForm(checkArr){ let _reg = null, ruleMsg, nullMsg, lenMsg; for (let i = 0, len = checkArr.length; i < len; i++) { //如果沒字段值是undefined,不再執(zhí)行當(dāng)前循環(huán),執(zhí)行下一次循環(huán) if (checkArr[i].el === undefined) { continue; } //設(shè)置規(guī)則錯誤提示信息 ruleMsg = checkArr[i].msg || "字段格式錯誤"; //設(shè)置值為空則錯誤提示信息 nullMsg = checkArr[i].nullMsg || "字段不能為空"; //設(shè)置長度錯誤提示信息 lenMsg = checkArr[i].lenMsg || "字段長度范圍" + checkArr[i].minLength + "至" + checkArr[i].maxLength; //如果該字段有空值校驗 if (checkArr[i].noNull === true) { //如果字段為空,返回結(jié)果又提示信息 if (checkArr[i].el === "" || checkArr[i].el === null) { return nullMsg; } } //如果有該字段有規(guī)則校驗 if (checkArr[i].rule) { //設(shè)置規(guī)則 switch (checkArr[i].rule) { case "mobile": _reg = /^1[3|4|5|7|8][0-9]d{8}$/; break; case "tel": _reg = /^d{3}-d{8}|d{4}-d{7}|d{11}$/; break; } //如果字段不為空,并且規(guī)則錯誤,返回錯誤信息 if (!_reg.test(checkArr[i].el) && checkArr[i].el !== "" && checkArr[i].el !== null) { return ruleMsg; } } //如果字段不為空并且長度錯誤,返回錯誤信息 if (checkArr[i].el !== null && checkArr[i].el !== "" && (checkArr[i].minLength || checkArr[i].maxLength)) { if (checkArr[i].el.toString().length < checkArr[i].minLength || checkArr[i].el.toString().length > checkArr[i].maxLength) { return lenMsg; } } } return false; }
函數(shù)調(diào)用方式
let testData={ phone:"18819323632", pwd:"112" } let _tips = validateForm([ {el: testData.phone, noNull: true, nullMsg: "電話號碼不能為空",rule: "mobile", msg: "電話號碼格式錯誤"}, {el: testData.pwd, noNull: true, nullMsg: "密碼不能為空",lenMsg:"密碼長度不正確",minLength:6,maxLength:18} ]); //字段驗證如果返回錯誤信息 if (_tips) { alert(_tips); }3-2.存在問題
這樣方法,相信大家看的也難受,因為問題確實是比較多。
1.一個字段進(jìn)入,可能要經(jīng)過三種判斷(空值,規(guī)則,長度)。如果只是一個簡單的電話號碼規(guī)則校驗,就要經(jīng)過其他兩種沒必要的校驗,造成不必要的開銷。運行的流程就如同下面。
2.規(guī)則校驗里面,只有這幾種校驗,如果要增加其他校驗,比如增加一個日期的規(guī)則,無法完成。如果一直修改源碼,可能會導(dǎo)致函數(shù)巨大。
3.寫法不優(yōu)雅,調(diào)用也不方便。
3-3.代替方案針對上面2-2的三個問題,逐個進(jìn)行改善。
因為調(diào)用方式就不方便,很難在不改變validateForm調(diào)用方式的同時,優(yōu)化重構(gòu)內(nèi)部的代碼,又增加擴(kuò)展性。重寫這個方法又不可能,因為有個別的地方已經(jīng)使用了這個API,自己一個一個的改不現(xiàn)實,所以就不修改這個validateForm,新建一個新的API:validate。在以后的項目上,也盡量引導(dǎo)同事放棄validateForm,使用新的API。
上面第一個,優(yōu)化校驗規(guī)則,每次校驗(比如空值,長度,規(guī)則),都是一個簡單的校驗,不再執(zhí)行其他沒必要的校驗。運行流程如同下面。
let validate = function (arr) { let ruleData = { /** * @description 不能為空 * @param val * @param msg * @return {*} */ isNoNull(val, msg){ if (!val) { return msg } }, /** * @description 最小長度 * @param val * @param length * @param msg * @return {*} */ minLength(val, length, msg){ if (val.toString().length < length) { return msg } }, /** * @description 最大長度 * @param val * @param length * @param msg * @return {*} */ maxLength(val, length, msg){ if (val.toString().length > length) { return msg } }, /** * @description 是否是手機(jī)號碼格式 * @param val * @param msg * @return {*} */ isMobile(val, msg){ if (!/^1[3-9]d{9}$/.test(val)) { return msg } } } let ruleMsg, checkRule, _rule; for (let i = 0, len = arr.length; i < len; i++) { //如果字段找不到 if (arr[i].el === undefined) { return "字段找不到!" } //遍歷規(guī)則 for (let j = 0; j < arr[i].rules.length; j++) { //提取規(guī)則 checkRule = arr[i].rules[j].rule.split(":"); _rule = checkRule.shift(); checkRule.unshift(arr[i].el); checkRule.push(arr[i].rules[j].msg); //如果規(guī)則錯誤 ruleMsg = ruleData[_rule].apply(null, checkRule); if (ruleMsg) { //返回錯誤信息 return ruleMsg; } } } }; let testData = { name: "", phone: "18819522663", pw: "asda" } //校驗函數(shù)調(diào)用 console.log(validate([ { //校驗的數(shù)據(jù) el: testData.phone, //校驗的規(guī)則 rules: [ {rule: "isNoNull", msg: "電話不能為空"}, {rule: "isMobile", msg: "手機(jī)號碼格式不正確"} ] }, { el: testData.pw, rules: [ {rule: "isNoNull", msg: "電話不能為空"}, {rule:"minLength:6",msg:"密碼長度不能小于6"} ] } ]));
如果又有其它的規(guī)則,又得改這個,這樣就違反了開放-封閉原則。如果多人共用這個函數(shù),規(guī)則可能會很多,ruleData會變的巨大,造成不必要的開銷。比如A頁面有金額的校驗,但是只有A頁面有。如果按照上面的方式改,在B頁面也會加載金額的校驗規(guī)則,但是根本不會用上,造成資源浪費。
所以下面應(yīng)用開放-封閉原則。給函數(shù)的校驗規(guī)則增加擴(kuò)展性。在實操之前,大家應(yīng)該會懵,因為一個函數(shù),可以進(jìn)行校驗的操作,又有增加校驗規(guī)則的操作。一個函數(shù)做兩件事,就違反了單一原則。到時候也難維護(hù),所以推薦的做法就是分接口做。如下寫法。
let validate = (function () { let ruleData = { /** * @description 不能為空 * @param val * @param msg * @return {*} */ isNoNull(val, msg){ if (!val) { return msg } }, /** * @description 最小長度 * @param val * @param length * @param msg * @return {*} */ minLength(val, length, msg){ if (val.toString().length < length) { return msg } }, /** * @description 最大長度 * @param val * @param length * @param msg * @return {*} */ maxLength(val, length, msg){ if (val.toString().length > length) { return msg } }, /** * @description 是否是手機(jī)號碼格式 * @param val * @param msg * @return {*} */ isMobile(val, msg){ if (!/^1[3-9]d{9}$/.test(val)) { return msg } } } return { /** * @description 查詢接口 * @param arr * @return {*} */ check: function (arr) { let ruleMsg, checkRule, _rule; for (let i = 0, len = arr.length; i < len; i++) { //如果字段找不到 if (arr[i].el === undefined) { return "字段找不到!" } //遍歷規(guī)則 for (let j = 0; j < arr[i].rules.length; j++) { //提取規(guī)則 checkRule = arr[i].rules[j].rule.split(":"); _rule = checkRule.shift(); checkRule.unshift(arr[i].el); checkRule.push(arr[i].rules[j].msg); //如果規(guī)則錯誤 ruleMsg = ruleData[_rule].apply(null, checkRule); if (ruleMsg) { //返回錯誤信息 return ruleMsg; } } } }, /** * @description 添加規(guī)則接口 * @param type * @param fn */ addRule:function (type,fn) { ruleData[type]=fn; } } })(); //校驗函數(shù)調(diào)用-測試用例 console.log(validate.check([ { //校驗的數(shù)據(jù) el: testData.mobile, //校驗的規(guī)則 rules: [ {rule: "isNoNull", msg: "電話不能為空"}, {rule: "isMobile", msg: "手機(jī)號碼格式不正確"} ] }, { el: testData.password, rules: [ {rule: "isNoNull", msg: "電話不能為空"}, {rule:"minLength:6",msg:"密碼長度不能小于6"} ] } ])); //擴(kuò)展-添加日期范圍校驗 validate.addRule("isDateRank",function (val,msg) { if(new Date(val[0]).getTime()>=new Date(val[1]).getTime()){ return msg; } }); //測試新添加的規(guī)則-日期范圍校驗 console.log(validate.check([ { el:["2017-8-9 22:00:00","2017-8-8 24:00:00"], rules:[{ rule:"isDateRank",msg:"日期范圍不正確" }] } ]));
如上代碼所示,這里需要往ruleData添加日期范圍的校驗,這里可以添加。但是不能訪問和修改ruleData的東西,有一個保護(hù)的作用。還有一個就是,比如在A頁面添加日期的校驗,只在A頁面存在,不會影響其它頁面。如果日期的校驗在其它地方都可能用上,就可以考慮,在全局里面為ruleData添加日期的校驗的規(guī)則。
至于第三個問題,這樣的想法,可能不算太優(yōu)雅,調(diào)用也不是太方便,但是就我現(xiàn)在能想到的,這個就是最好方案啊了。
這個看似是已經(jīng)做完了,但是大家可能覺得有一種情況沒能應(yīng)對,比如下面這種,做不到。
因為上面的check接口,只要有一個錯誤了,就立馬跳出了,不會校驗下一個。如果要實現(xiàn)下面的功能,就得實現(xiàn),如果有一個值校驗錯誤,就記錄錯誤信息,繼續(xù)校驗下一個,等到所有的校驗都執(zhí)行完了之后,如下面的流程圖。
代碼上面(大家先忽略alias這個屬性)
let validate= (function () { let ruleData = { /** * @description 不能為空 * @param val * @param msg * @return {*} */ isNoNull(val, msg){ if (!val) { return msg } }, /** * @description 最小長度 * @param val * @param length * @param msg * @return {*} */ minLength(val, length, msg){ if (val.toString().length < length) { return msg } }, /** * @description 最大長度 * @param val * @param length * @param msg * @return {*} */ maxLength(val, length, msg){ if (val.toString().length > length) { return msg } }, /** * @description 是否是手機(jī)號碼格式 * @param val * @param msg * @return {*} */ isMobile(val, msg){ if (!/^1[3-9]d{9}$/.test(val)) { return msg } } } return { check: function (arr) { //代碼不重復(fù)展示,上面一部分 }, addRule:function (type,fn) { //代碼不重復(fù)展示,上面一部分 }, /** * @description 校驗所有接口 * @param arr * @return {*} */ checkAll: function (arr) { let ruleMsg, checkRule, _rule,msgArr=[]; for (let i = 0, len = arr.length; i < len; i++) { //如果字段找不到 if (arr[i].el === undefined) { return "字段找不到!" } //如果字段為空以及規(guī)則不是校驗空的規(guī)則 //遍歷規(guī)則 for (let j = 0; j < arr[i].rules.length; j++) { //提取規(guī)則 checkRule = arr[i].rules[j].rule.split(":"); _rule = checkRule.shift(); checkRule.unshift(arr[i].el); checkRule.push(arr[i].rules[j].msg); //如果規(guī)則錯誤 ruleMsg = ruleData[_rule].apply(null, checkRule); if (ruleMsg) { //記錄錯誤信息 msgArr.push({ el:arr[i].el, alias:arr[i].alias, rules:_rule, msg:ruleMsg }); } } } //返回錯誤信息 return msgArr.length>0?msgArr:false; } } })(); let testData = { name: "", phone: "188", pw: "asda" } //擴(kuò)展-添加日期范圍校驗 validate.addRule("isDateRank",function (val,msg) { if(new Date(val[0]).getTime()>=new Date(val[1]).getTime()){ return msg; } }); //校驗函數(shù)調(diào)用 console.log(validate.checkAll([ { //校驗的數(shù)據(jù) el: testData.phone, alias:"mobile", //校驗的規(guī)則 rules: [ {rule: "isNoNull", msg: "電話不能為空"}, {rule: "isMobile", msg: "手機(jī)號碼格式不正確"},{rule:"minLength:6",msg: "手機(jī)號碼不能少于6"} ] }, { el: testData.pw, alias:"pwd", rules: [ {rule: "isNoNull", msg: "電話不能為空"}, {rule:"minLength:6",msg:"密碼長度不能小于6"} ] }, { el:["2017-8-9 22:00:00","2017-8-8 24:00:00"], rules:[{ rule:"isDateRank",msg:"日期范圍不正確" }] } ]));
看到結(jié)果,現(xiàn)在所有的不合法的數(shù)據(jù)的記錄都返回回來了。至于當(dāng)時alias現(xiàn)在揭曉用處。
比如頁面是vue渲染的,根據(jù)alias可以這樣處理。
如果是jQuery渲染的,根據(jù)alias可以這樣處理。
3-4.向下兼容方案因為項目之前有使用了以前的校驗API,不能一道切,在以前的API沒廢棄之前,不能影響之前的使用。所以要重寫以前的validateForm,使之兼容現(xiàn)在的新API:validate。
let validateForm=function (arr) { let _param=[],_single={}; for(let i=0;i4.小結(jié) 今天的例子就到這里了,這個例子,無非就是給API增加擴(kuò)展性。這個例子比較簡單,不算難。大家用這個代碼在瀏覽器上運行,就很好理解。如果大家對這個例子有什么更好的建議,或者代碼上有什么問題,歡迎在評論區(qū)留言,大家多交流,相互學(xué)習(xí)。
-------------------------華麗的分割線--------------------
想了解更多,關(guān)注關(guān)注我的微信公眾號:守候書閣
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/94783.html
摘要:綜上所述,為使傳統(tǒng)應(yīng)用程序容器化,有以下幾種路徑擴(kuò)展重構(gòu)或者重建。在中運行應(yīng)用程序的最大障礙之一是臨時文件系統(tǒng)。大體來說,利用容器技術(shù)實現(xiàn)傳統(tǒng)應(yīng)用程序的現(xiàn)代化并沒有硬性規(guī)則。 技術(shù)領(lǐng)域是不斷變化的,因此,任何應(yīng)用程序都可能在很短時間內(nèi)面臨過時甚至淘汰,更新?lián)Q代的速度之快給人的感覺越來越強(qiáng)烈,我們?nèi)绾问箓鹘y(tǒng)應(yīng)用程序保持活力不落伍?工程師想的可能是從頭開始重建傳統(tǒng)應(yīng)用程序,這與公司的業(yè)務(wù)目...
摘要:微服務(wù)集成服務(wù)間通信微服務(wù)架構(gòu)下,應(yīng)用的服務(wù)直接相互獨立。微服務(wù)架構(gòu)傾向于降低中心消息總線類似于的依賴,將業(yè)務(wù)邏輯分布在每個具體的服務(wù)終端。 引言:微服務(wù)是當(dāng)前軟件架構(gòu)領(lǐng)域非常熱門的詞匯,能找到很多關(guān)于微服務(wù)的定義、準(zhǔn)則,以及如何從微服務(wù)中獲益的文章,在企業(yè)的實踐中去應(yīng)用微服務(wù)的資源卻很少。本篇文章中,會介紹微服務(wù)架構(gòu)(Microservices Architecture)的基礎(chǔ)概念,...
摘要:微服務(wù)集成服務(wù)間通信微服務(wù)架構(gòu)下,應(yīng)用的服務(wù)直接相互獨立。微服務(wù)架構(gòu)傾向于降低中心消息總線類似于的依賴,將業(yè)務(wù)邏輯分布在每個具體的服務(wù)終端。 引言:微服務(wù)是當(dāng)前軟件架構(gòu)領(lǐng)域非常熱門的詞匯,能找到很多關(guān)于微服務(wù)的定義、準(zhǔn)則,以及如何從微服務(wù)中獲益的文章,在企業(yè)的實踐中去應(yīng)用微服務(wù)的資源卻很少。本篇文章中,會介紹微服務(wù)架構(gòu)(Microservices Architecture)的基礎(chǔ)概念,...
摘要:相反,它由單體中的適配器和使用一個或多個進(jìn)程間通信機(jī)制的服務(wù)組成。因為微服務(wù)架構(gòu)的本質(zhì)是一組圍繞業(yè)務(wù)功能組織的松耦合服務(wù)。如果你嘗試將此類功能實現(xiàn)為服務(wù),則通常會發(fā)現(xiàn),由于過多的進(jìn)程間通信而導(dǎo)致性能下降。這是快速展示微服務(wù)架構(gòu)價值的好方法。你很有可能正在處理大型復(fù)雜的單體應(yīng)用程序,每天開發(fā)和部署應(yīng)用程序的經(jīng)歷都很緩慢而且很痛苦。微服務(wù)看起來非常適合你的應(yīng)用程序,但它也更像是一項遙不可及的必殺...
摘要:摘要的生態(tài)地位已經(jīng)確立,可擴(kuò)展性將是其發(fā)力的主戰(zhàn)場。該功能由于只是替代了做了些更名的工作,所以在已經(jīng)是穩(wěn)定的狀態(tài)了。異構(gòu)計算作為非常重要的新戰(zhàn)場,非常重視。而異構(gòu)計算需要強(qiáng)大的計算力和高性能網(wǎng)絡(luò),需要提供一種統(tǒng)一的方式與等高性能硬件集成。 摘要: Kubernetes的生態(tài)地位已經(jīng)確立,可擴(kuò)展性將是其發(fā)力的主戰(zhàn)場。異構(gòu)計算作為非常重要的新戰(zhàn)場,Kubernetes非常重視。而異構(gòu)計算需...
閱讀 3149·2021-11-22 13:54
閱讀 3435·2021-11-15 11:37
閱讀 3598·2021-10-14 09:43
閱讀 3496·2021-09-09 11:52
閱讀 3595·2019-08-30 15:53
閱讀 2457·2019-08-30 13:50
閱讀 2054·2019-08-30 11:07
閱讀 885·2019-08-29 16:32