摘要:編寫可維護(hù)的代碼前言我們?cè)谛薷乃舜a的時(shí)候,閱讀他人代碼所花的時(shí)間經(jīng)常比實(shí)現(xiàn)功能的時(shí)間還要更多如果程序結(jié)構(gòu)不清晰,代碼混亂。這樣可以去除重復(fù)的代碼,提高靈活性關(guān)鍵點(diǎn)找出不同的地方和重復(fù)的地方。
編寫可維護(hù)的代碼 前言
我們?cè)谛薷乃舜a的時(shí)候,閱讀他人代碼所花的時(shí)間經(jīng)常比實(shí)現(xiàn)功能的時(shí)間還要更多
如果程序結(jié)構(gòu)不清晰,代碼混亂 。牽一發(fā)而動(dòng)全身。那維護(hù)起來(lái)就更難維護(hù)了
可讀性可理解性:他人可以接手代碼并理解它
直觀性 : 代碼邏輯清晰
可調(diào)試性 :出錯(cuò)時(shí),方便定位問(wèn)題所在
如何提高可讀性代碼格式化
適當(dāng)添加注釋
函數(shù)與方法
大段代碼
注釋需有意義
如何優(yōu)化代碼找出代碼的壞味道
使用重構(gòu)手法將其解決掉
代碼的壞味道在我們的程序中,可以聞到很多的壞味道。主要有以下這些點(diǎn)
命名不規(guī)范或無(wú)意義命名存在使用縮寫、不規(guī)范、無(wú)意義
例子:var a = xxx,b = xxx
重復(fù)代碼相同(或相似)的代碼在項(xiàng)目中出現(xiàn)了多次,如果需求發(fā)生更改,則需要同時(shí)修改多個(gè)地方
過(guò)長(zhǎng)函數(shù)程序越長(zhǎng)越難理解,一個(gè)函數(shù)應(yīng)該只完成一個(gè)功能
過(guò)長(zhǎng)的類一個(gè)類的職責(zé)過(guò)多,一個(gè)類應(yīng)該是一個(gè)獨(dú)立的整體。
過(guò)長(zhǎng)參數(shù)列表太長(zhǎng)的參數(shù)列表難以理解,不易使用。當(dāng)需要修改的時(shí)候,會(huì)更加容易出錯(cuò)
數(shù)據(jù)泥團(tuán)有些數(shù)據(jù)項(xiàng)總是成群結(jié)隊(duì)的待在一起。例如兩個(gè)類中相同的字段、許多函數(shù)簽名相同的參數(shù)。
這些都應(yīng)該提煉到一個(gè)對(duì)象中,將很多參數(shù)列縮短,簡(jiǎn)化函數(shù)調(diào)用
類似的函數(shù)整體上實(shí)現(xiàn)的功能差不多,但是由于有一點(diǎn)點(diǎn)區(qū)別。所以寫成了多個(gè)函數(shù)
重構(gòu)手法 提煉函數(shù)針對(duì)一個(gè)比較長(zhǎng)的函數(shù),提煉成一個(gè)個(gè)完成特定功能的函數(shù)。
例子// 提煉前 function test11() { var day = $("day"); var yearVal = "2016"; var monthVal = "10"; var dayVal = "10"; day.val(dayVal); switch (monthVal) { case 4: case 6: case 9: case 11: if (dayVal > 30) { day.val(30); } break; case 2: if ( yearVal % 4 == 0 && (yearVal % 100 != 0 || yearVal % 400 == 0) && monthVal == 2 ) { if (dayVal > 29) { day.val(29); } } else { if (dayVal > 28) { day.val(28); } } break; default: if (dayVal > 31) { day.val(31); } } }
// 提煉后 function test12() { var day = $("day"); var yearVal = "2016"; var monthVal = "10"; var dayVal = "10"; var maxDay = getMaxDay(yearVal, monthVal); if (dayVal > maxDay) { day.val(maxDay); } else { day.val(dayVal); } } function getMaxDay(year, month) { var maxDay = 0; switch (month) { case 4: case 6: case 9: case 11: maxDay = 30; break; case 2: if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) { maxDay = 29; } else { maxDay = 28; } break; default: maxDay = 31; } return maxDay; }
例子中,提煉前的代碼,需要很費(fèi)勁的看完整個(gè)函數(shù),才會(huì)明白做了什么處理,提煉后的代碼。只需要稍微看一下,就知道 getMaxDay 是獲取當(dāng)前月份的最大天數(shù)優(yōu)點(diǎn):
如果每個(gè)函數(shù)的粒度都很小,那么函數(shù)被復(fù)用的機(jī)會(huì)就更大;
這會(huì)使高層函數(shù)讀起來(lái)就想一系列注釋;
如果函數(shù)都是細(xì)粒度,那么函數(shù)的覆寫也會(huì)更容易些
內(nèi)聯(lián)函數(shù)有時(shí)候,一個(gè)函數(shù)的本體與函數(shù)名一樣簡(jiǎn)單易懂,就要用到這種手法。
這種手法用于處理優(yōu)化過(guò)度的問(wèn)題
舉個(gè)例子:
function biggerThanZero(num) { return num > 0; } function test() { var num = 10; if (biggerThanZero(num)) { //do something } } //內(nèi)聯(lián)后 function test() { var num = 10; if (num > 0) { //do something } }引入解釋性變量
當(dāng)表達(dá)式比較復(fù)雜難以閱讀的時(shí)候,就可以通過(guò)臨時(shí)變量來(lái)幫助你將表達(dá)式分解為容易管理的形式
有些時(shí)候,運(yùn)用提煉函數(shù)會(huì)更好一點(diǎn)
舉兩個(gè)簡(jiǎn)單的例子:
// 例子 1 // before function test2() { if ( platform.toUpperCase().indexOf("MAC") > -1 && browser.toUpperCase().indexOf("IE") > -1 && wasInitialized() && resize > 0 ) { // do something } } // after function test2() { var isMacOs = platform.toUpperCase().indexOf("MAC") > -1; var isIEBrowser = browser.toUpperCase().indexOf("IE") > -1; var wasResized = resize > 0; if (isMacOs && isIEBrowser && wasInitialized() && wasResized) { // do something } } // -------------------------------------------------- // 例子2 // before function caluPrice(quantity, itemPrice) { return ( quantity * itemPrice - Math.max(0, quantity - 500) * itemPrice * 0.05 + Math.min(quantity * itemPrice * 0.1, 100) ); } // after function caluPrice(quantity, itemPrice) { var basePrice = quantity * itemPrice; var discount = Math.max(0, quantity - 500) * itemPrice * 0.05; var shiping = Math.min(basePrice * 0.1, 100); return basePrice - discount + shiping; }
在兩個(gè)例子中,引入解釋性的變量之后,可讀性大大增加。函數(shù)的意圖就比較明顯,單看變量命名就已經(jīng)能大概知道具體的實(shí)現(xiàn)分解臨時(shí)變量
除了 for 循環(huán)里用來(lái)收集結(jié)果的變量,其他的臨時(shí)變量都應(yīng)該只被賦值一次。
因?yàn)楸毁x值超過(guò)一次,就意味著他在函數(shù)中承擔(dān)了多個(gè)責(zé)任。
一個(gè)變量承擔(dān)多個(gè)責(zé)任。會(huì)令代碼看起來(lái)容易迷惑
舉個(gè)例子:
// 分解臨時(shí)變量 // before function test3() { var temp = 2 * (width + height); console.log(temp); // do something temp = height * width; // do something console.log(temp); } // after function test4() { var perimeter = 2 * (width + height); console.log(perimeter); // do something var area = height * width; // do something console.log(area); }
在這個(gè)例子中,temp 分別被賦予了兩次,如果代碼塊較長(zhǎng)的情況,會(huì)增加風(fēng)險(xiǎn),因?yàn)槟悴恢浪谀睦锉桓牡袅?/pre> 替換算法當(dāng)你重構(gòu)的時(shí)候,發(fā)現(xiàn)實(shí)現(xiàn)同樣的功能有一個(gè)更清晰的方式,就應(yīng)該將原有的算法替換成你的算法。
舉個(gè)例子:
// 替換算法 // before function getWeekDay() { var weekStr = ""; switch (date.format("d")) { case 0: weekStr = "日"; break; case 1: weekStr = "一"; break; case 2: weekStr = "二"; break; case 3: weekStr = "三"; break; case 4: weekStr = "四"; break; case 5: weekStr = "五"; break; case 6: weekStr = "六"; break; } return weekStr; } // after function getWeekDay() { var weekDays = ["日", "一", "二", "三", "四", "五", "六"]; return weekDays[date.format("d")]; }以字面常量取代魔法數(shù)(eg:狀態(tài)碼)在計(jì)算機(jī)科學(xué)中,魔法數(shù)是歷史最悠久的不良現(xiàn)象之一。
魔法數(shù)是指程序中莫名其妙的數(shù)字。擁有特殊意義,卻又不能明確表現(xiàn)出這種意義的數(shù)字舉個(gè)例子:
// before function test5(x) { if (x == 1) { console.log("完成"); } else if (x == 2) { console.log("上傳中"); } else if (x == 3) { console.log("上傳失敗"); } else { console.log("未知的錯(cuò)誤"); } } function test6(x) { if (x == 3) { // do something } } // after var UploadStatus = { START: 0, UPLOADING: 1, SUCCESS: 2, ERROR: 3, UNKNOWN: 4 }; function test7(x) { if (x == UploadStatus.START) { console.log("未開始"); } else if (x == UploadStatus.UPLOADING) { console.log("上傳中"); } else if (x == UploadStatus.SUCCESS) { console.log("上傳成功"); } else if (x == UploadStatus.ERROR) { console.log("上傳失敗"); } else { console.log("未知的錯(cuò)誤"); } } function test8(x) { if (x == UploadStatus.ERROR) { // do something } }對(duì)于魔法數(shù),應(yīng)該用一個(gè)枚舉對(duì)象或一個(gè)常量來(lái)賦予其可見的意義。這樣,你在用到的時(shí)候,就能夠明確的知道它代表的是什么意思分解條件表達(dá)式
而且,當(dāng)需求變化的時(shí)候,只需要改變一個(gè)地方即可復(fù)雜的條件邏輯是導(dǎo)致復(fù)雜度上升的地點(diǎn)之一。因?yàn)楸仨毦帉懘a來(lái)處理不同的分支,很容易就寫出一個(gè)相當(dāng)長(zhǎng)的函數(shù)
將每個(gè)分支條件分解成新函數(shù)可以突出條件邏輯,更清楚表明每個(gè)分支的作用以及原因
舉個(gè)例子:
// 分解條件表達(dá)式 // 商品在冬季和夏季單價(jià)不一樣 // before var SUMMER_START = "06-01"; var SUMMER_END = "09-01"; function test9() { var quantity = 2; var winterRate = 0.5; var winterServiceCharge = 9; var summerRate = 0.6; var charge = 0; if (date.before(SUMMER_START) || date.after(SUMMER_END)) { charge = quantity * winterRate + winterServiceCharge; } else { charge = quantity * summerRate; } return charge; } // after function test9() { var quantity = 2; return notSummer(date) ? winterCharge(quantity) : summerCharge(quantity); } function notSummer(date) { return date.before(SUMMER_START) || date.after(SUMMER_END); } function summerCharge(quantity) { var summerRate = 0.6; return quantity * summerRate; } function winterCharge(quantity) { var winterRate = 0.5; var winterServiceCharge = 9; return quantity * winterRate + winterServiceCharge; }合并條件表達(dá)式當(dāng)發(fā)現(xiàn)一系列的條件檢查,檢查條件不一樣,但是行為卻一致。就可以將它們合并為一個(gè)條件表達(dá)式
舉個(gè)例子:
// 合并條件表達(dá)式 // before function test10(x) { var isFireFox = "xxxx"; var isIE = "xxxx"; var isChrome = "xxxx"; if (isFireFox) { return true; } if (isIE) { return true; } if (isChrome) { return true; } return false; } // after function test10(x) { var isFireFox = "xxxx"; var isIE = "xxxx"; var isChrome = "xxxx"; if (isFireFox || isIE || isChrome) { return true; } return false; }合并后的代碼會(huì)告訴你,實(shí)際上只有一個(gè)條件檢查,只是有多個(gè)并列條件需要檢查而已合并重復(fù)的條件片段條件表達(dá)式上有著相同的一段代碼,就應(yīng)該將它搬離出來(lái)
// 合并重復(fù)片段 // before function test11(isSpecial) { var total, price = 1; if (isSpecial) { total = price * 0.95; // 這里處理一些業(yè)務(wù) } else { total = price * 0.8; // 這里處理一些業(yè)務(wù) } } // after function test12(isSpecial) { var total, price = 1; if (isSpecial) { total = price * 0.95; } else { total = price * 0.8; } // 這里處理一些業(yè)務(wù) }在不同的條件里面做了同樣的事情,應(yīng)該將其抽離出條件判斷。這樣代碼量少而且邏輯更加清晰以衛(wèi)語(yǔ)句取代嵌套條件表達(dá)式如果某個(gè)條件較為罕見,應(yīng)該多帶帶檢查該條件,并在該條件為真時(shí)立即從函數(shù)中返回。這樣的檢查就叫衛(wèi)語(yǔ)句
舉個(gè)例子:
// 以衛(wèi)語(yǔ)句取代嵌套條件表達(dá)式 // before function getPayMent() { var result = 0; if (isDead) { result = deadAmount(); } else { if (isSepartated) { result = separtedAmount(); } else { if (isRetired) { result = retiredAmount(); } else { result = normalPayAmount(); } } } return result; } // after function getPayMent() { if (isDead) { return deadAmount(); } if (isSepartated) { return separtedAmount(); } if (isRetired) { return retiredAmount(); } return normalPayAmount(); }函數(shù)改名(命名)當(dāng)函數(shù)名稱不能表達(dá)函數(shù)的用途,就應(yīng)該改名
變量和函數(shù)應(yīng)使用合乎邏輯的名字。
eg:獲取產(chǎn)品列表 -> getProductList()
變量名應(yīng)為名詞,因?yàn)樽兞棵枋龅拇蟛糠质且粋€(gè)事物。
eg: 產(chǎn)品 -> product
函數(shù)名應(yīng)為動(dòng)詞開始,因?yàn)楹瘮?shù)描述的是一個(gè)動(dòng)作
eg:獲取產(chǎn)品列表 -> getProductList()將查詢函數(shù)和修改函數(shù)分開如果某個(gè)函數(shù)只向你提供一個(gè)值,沒(méi)有任何副作用。這個(gè)函數(shù)就可以任意的調(diào)用。
這樣的函數(shù)稱為純函數(shù)如果遇到一個(gè)既有返回值,又有副作用的函數(shù)。就應(yīng)該將查詢與修改動(dòng)作分離出來(lái)
舉個(gè)例子:
// before function test13(people) { for (var i = 0, len = people.length; i < len; i++) { if (people[i].name == "andy") { // do something 例如進(jìn)行DOM 操作之類的 return "andy"; } if (people[i].name == "ChunYang") { // do something 例如進(jìn)行DOM 操作之類的 return "ChunYang"; } } } // after function test14(people) { var p = find(people); // do something 例如進(jìn)行DOM 操作之類的 // doSomeThing(p); } function find(people) { for (var i = 0, len = people.length; i < len; i++) { if (people[i].name == "andy") { return "andy"; } if (people[i].name == "ChunYang") { return "ChunYang"; } } }令函數(shù)攜帶參數(shù)如果發(fā)現(xiàn)兩個(gè)函數(shù),做著類似的工作。區(qū)別只在于其中幾個(gè)變量的不同。就可以通過(guò)參數(shù)來(lái)處理。
這樣可以去除重復(fù)的代碼,提高靈活性
關(guān)鍵點(diǎn): 找出不同的地方和重復(fù)的地方。
推薦書籍《重構(gòu) 改善既有代碼的設(shè)計(jì) 》 基于 java 的
《代碼大全》
相關(guān)鏈接個(gè)人博客
代碼片段
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/100248.html
摘要:沒(méi)有初始化的變量都會(huì)賦值為盡量避免使用因?yàn)闆](méi)有聲明的變量也會(huì)判斷為類型。對(duì)象直接量,不建議使用構(gòu)造函數(shù)創(chuàng)建對(duì)象數(shù)組直接量,不建議使用構(gòu)造函數(shù)創(chuàng)建數(shù)組 編寫可維護(hù)代碼的重要性 程序是給人讀的,只是偶爾給機(jī)器運(yùn)行一下 1、軟件生命周期的80%成本是發(fā)生在為維護(hù)上;2、幾乎所有的軟件維護(hù)者都不是最初的創(chuàng)建者;3、編寫規(guī)范提高了軟件代碼的可讀性,它讓軟件工程師快速充分的理解代碼; 編寫規(guī)范 縮...
摘要:最近讀完編寫可維護(hù)的,讓我受益匪淺,它指明了編碼過(guò)程中,需要注意的方方面面,在團(tuán)隊(duì)協(xié)作中特別有用,可維護(hù)性是一個(gè)非常大的話題,這本書是一個(gè)不錯(cuò)的起點(diǎn)。擴(kuò)展閱讀編寫可維護(hù)的歡迎來(lái)到石佳劼的博客,如有疑問(wèn),請(qǐng)?jiān)谠脑u(píng)論區(qū)留言,我會(huì)盡量為您解答。 最近讀完《編寫可維護(hù)的JavaScript》,讓我受益匪淺,它指明了編碼過(guò)程中,需要注意的方方面面,在團(tuán)隊(duì)協(xié)作中特別有用,可維護(hù)性是一個(gè)非常大的話...
摘要:與此類似,理所當(dāng)然的,我們程序員也會(huì)有自己的圣經(jīng)。這便是程序員的圣經(jīng)三個(gè)原則我認(rèn)為做為一個(gè)程序員,最神圣的就是三個(gè)原則,它幾乎能完整無(wú)誤的定義做為一個(gè)程序員應(yīng)該如何去編碼。 ...
摘要:寫在前面新司機(jī)最近讀完編寫可維護(hù)的,學(xué)到不少東西。書分為編程風(fēng)格編程實(shí)踐自動(dòng)化三個(gè)部分。編程風(fēng)格并不是絕對(duì)的,每個(gè)人或團(tuán)隊(duì)都有自己的編程風(fēng)格,但知道哪些地方需要注意的話,還是有助于新司機(jī)完成代碼風(fēng)格的轉(zhuǎn)變。 寫在前面 新司機(jī)最近讀完《編寫可維護(hù)的JavaScript》,學(xué)到不少東西。書分為編程風(fēng)格、編程實(shí)踐、自動(dòng)化三個(gè)部分。其中編程風(fēng)格是你的代碼格式約定,統(tǒng)一的格式不僅僅有利于團(tuán)隊(duì),也...
摘要:所有的塊語(yǔ)句都應(yīng)當(dāng)使用花括號(hào)包括花括號(hào)的對(duì)齊方式第一種風(fēng)格第二種風(fēng)格塊語(yǔ)句間隔第一種在語(yǔ)句名圓括號(hào)和左花括號(hào)之間沒(méi)有空格間隔第二種在左圓括號(hào)之前和右圓括號(hào)之后各添加一個(gè)空格第三種在左圓括號(hào)后和右圓括號(hào)前各添加一個(gè)空格我個(gè)人喜歡在右括號(hào)之后添 所有的塊語(yǔ)句都應(yīng)當(dāng)使用花括號(hào), 包括: if for while do...while... try...catch...finally 3....
閱讀 761·2021-11-17 09:33
閱讀 3804·2021-09-01 10:46
閱讀 1786·2019-08-30 11:02
閱讀 3313·2019-08-29 15:05
閱讀 1427·2019-08-26 11:39
閱讀 2308·2019-08-23 17:04
閱讀 1998·2019-08-23 15:43
閱讀 1397·2019-08-23 14:12