改善代碼質(zhì)量,試試這十種方法
你好,我是猿java。
作為一名 Java程序員,如果把代碼比作孩子,代碼質(zhì)量就如同孩子的健康,因此,作為技術(shù)人員,日常工作勢必堅守兩條紅線:
- 保證代碼交付質(zhì)量
- 保證代碼交付時間
對于這兩條紅線,其實不難理解,總結(jié)成一句話:在規(guī)定的時間內(nèi)交付高質(zhì)量的代碼。
那么,什么是高質(zhì)量的代碼?如何才能寫出高質(zhì)量的代碼?為什么有的程序員工作 5年,寫出來的代碼質(zhì)量還不如 3年的程序員?今天我們就來聊一聊。
一、什么是高質(zhì)量代碼
代碼的“好”與“壞”是一個相對的描述,因此,高質(zhì)量代碼也是一個很寬泛的概念,很難給它下一個精準(zhǔn)的定義,但是,我們可以結(jié)合日常的開發(fā)工作經(jīng)驗,給出幾個常見的衡量維度:
1. 可讀性
高質(zhì)量的代碼首先需要具備可讀性,我們編寫的代碼除了需要讓機(jī)器能編譯運(yùn)行之外,更需要讓程序員讀懂,因為只有程序員讀懂了它,才能更好地修 bug,添加新功能,做后期維護(hù)等等。
可能有小伙伴會說,Spring的源碼很難讀懂,因此它不具備可讀性,這種理解是錯誤的,Spring框架是 Java生態(tài)中比較優(yōu)秀的開源代碼,讀不懂是因為“內(nèi)功”不夠,本文的可讀性是指代碼命名是否規(guī)范,注釋是否合理,分層是否清晰以及是否具備高內(nèi)聚低耦合等特性。
2. 可維護(hù)性
現(xiàn)實工作中,很難遇見一次性代碼(開發(fā)部署完之后再也不需要迭代),大部分情況都需要在一個模塊上不斷地迭代新功能和新代碼,因此,高質(zhì)量的代碼必須具備可維護(hù)性。
可維護(hù)性反饋到代碼上,可以通俗地表達(dá)成:bug能修改,老代碼改得動,新功能可添加。而這些更改花費(fèi)的代價就體現(xiàn)了可維護(hù)的難和易,比如:改動是否會增加大量的新 bug,改動對現(xiàn)有的邏輯的破壞性有多大,或者說改動的時間是否會很長。
3. 可擴(kuò)展性
在代碼設(shè)計 SOLID 中有一個很重要的原則就是開閉原則,要求代碼需要 “對修改關(guān)閉,對擴(kuò)展開放”,因此,高質(zhì)量的代碼需要具備可擴(kuò)展性。在應(yīng)對業(yè)務(wù)迭代時,開發(fā)者需要多關(guān)注代碼能否用最小的改動來適配新的功能。
4. 可復(fù)用性
在日常開發(fā)中,盡量不重復(fù)造輪子,具體到代碼上,不應(yīng)該出現(xiàn)大量重復(fù)的代碼,代碼應(yīng)該保持簡潔,重復(fù)的代碼可以抽離出來,實現(xiàn)代碼復(fù)用。因此,可復(fù)用性也是衡量高質(zhì)量代碼的一個重要標(biāo)準(zhǔn)。
5. 可測試性
單測是開發(fā)人員保證代碼質(zhì)量的一個重要方法,因此,編寫的代碼是否具備可測試性,也是衡量代碼高質(zhì)量的一個標(biāo)準(zhǔn)。如果編寫的代碼很難寫單元測試,那是否意味著代碼設(shè)計存在很大的問題?
在分析了高質(zhì)量代碼的幾個常用衡量維度之后,我們可以總結(jié):所謂高質(zhì)量的代碼,其實就是衡量能否寫出可讀性好,易維護(hù),易擴(kuò)展,復(fù)用性高,可測試的代碼。
接下來提供十種高效提升代碼質(zhì)量的方法。
二、提升代碼質(zhì)量方法
1. 規(guī)范命名
眾所周知,一個好的名字可以使人受用一生,對于代碼也是如此,好的命名可以幫助讀者更好地理解代碼的功能。
對于計算機(jī)科班出身的小伙伴,或許在大學(xué)的第一節(jié)編程課就被強(qiáng)調(diào):代碼命名要簡明扼要并且見名知意,不要使用拼音,不要使用非公認(rèn)的縮寫等等,下面為代碼命名的幾點(diǎn)建議:
(1) 使用常見的英文單詞,不使用拼音
代碼的命名,應(yīng)該盡量使用常見的英文單詞,這樣對于大部分人來說能夠知道其意圖。不要使用拼音命名,在軟件開發(fā)領(lǐng)域,英文是主要的編程語言和命名規(guī)范。使用拼音給代碼命名違反了行業(yè)的慣例和標(biāo)準(zhǔn)而且可讀性差。
(2) 使用公認(rèn)的縮寫,否則使用全稱
有時候,為了簡化命名,會使用縮寫,但是這些縮寫一定要是業(yè)內(nèi)大家公認(rèn)的,比如:Impl 指代 Implement, str 指代 String,num 指代 number,del 指代 delete。
(3) 使用和團(tuán)隊一致的命名風(fēng)格
現(xiàn)在的開發(fā)大部分是團(tuán)隊合作,因此,代碼命名應(yīng)該在團(tuán)隊內(nèi)部保持一致,比如:和數(shù)據(jù)庫交互的 Reporitory層,一般都是 CRUD方法的封裝,如果團(tuán)隊使用 selectXXX 代表查詢,我們就不要使用getXXX 或者 queryXXX,這樣檢索查詢的方法時,可以根據(jù)select定位,同理,對于添加數(shù)據(jù)是使用 addXXX 還是 saveXXX 或者 insertXXX,也需要保持一種方式。
(4) 命名不要太長
命名要盡量的簡短,而且能準(zhǔn)確的傳達(dá)意思,如果需要使用長命名,建議不要超過5個單詞。
溫馨建議:如果你在日常工作中對于命名拿不準(zhǔn)時,可以給身邊的同事參考,看看他們對該命名是否和你的本意是一致,這樣也能幫助你更好的去命名。
2. 巧用注釋
命名可以幫助讀者從字面上了解代碼的功能,但無法傳達(dá)代碼更多細(xì)節(jié),因此,需要借助注釋來完成這部分功能。喜歡查看源碼的小伙伴應(yīng)該可以發(fā)現(xiàn),在 JDK,Spring等這些優(yōu)秀的開源框架的源碼中,類,方法甚至變量,幾乎都有相應(yīng)的注釋,而我們在使用某個功能時,最直接的方式往往是通過官方提供的注釋來了解其功能,因此,注釋的重要性可想而知。
因此,注釋是命名的補(bǔ)充和增強(qiáng)。
注釋需要寫什么內(nèi)容呢?這里以 java.util.Collections 為例, 如下圖:
通過 JDK 的 Collections源碼注釋可以看出:注釋的內(nèi)容大概包含 5部分信息:what, why, how, time, authors 即是什么?為什么?怎樣做?時間 和 作者。我們可以根據(jù)需要靈活的搭配來添加注釋。需要注意的,注釋也需要簡明扼要,盡量使用一些提煉,解釋和總結(jié)性的語句,切勿長篇大論。
3. 代碼風(fēng)格
一個好的代碼風(fēng)格可以讓代碼看起來很清爽,降低代碼閱讀的復(fù)雜度。代碼風(fēng)格主要體現(xiàn)在以下幾點(diǎn):左括號風(fēng)格,代碼縮進(jìn),空行,超長行,魔數(shù),方法代碼過多,方法參數(shù)過多
- 左括號風(fēng)格在代碼行尾還是換行使用左括號,這個根據(jù)個人習(xí)慣,不過團(tuán)隊合作要求保持一種風(fēng)格。
- 代碼縮進(jìn)代碼編寫時,為了美觀,一般都不會頂格,需要縮進(jìn),因此常見的縮進(jìn)有 兩格縮進(jìn)和四格縮進(jìn),選擇哪一種也是根據(jù)個人習(xí)慣,不過團(tuán)隊合作要求保持一種風(fēng)格。
- 空行在方法內(nèi)部,我們可以使用空行,將邏輯獨(dú)立的代碼塊區(qū)分,這樣,一方面可以更方便的閱讀方法實現(xiàn),另一方面,當(dāng)獨(dú)立的代碼塊代碼量增多時,可以考慮將代碼塊抽離成新的方法。
- 超長行每行的代碼量不要太長,通常建議電腦一屏的寬度為準(zhǔn),如果超過一屏,建議換行。
- 魔數(shù)在代碼編寫時,不要出現(xiàn)魔數(shù),我們要善于使用常量去定義這些魔數(shù),增加代碼的可讀性。
- 方法代碼過多每個方法中的代碼量不要太多,因為代碼太多會增加方法閱讀的復(fù)雜度,通常建議電腦一屏的高度為準(zhǔn),超過了就要考慮能否再抽離出新方法。
- 方法參數(shù)過多編寫方法的時候,通常都會定義入?yún)ⅲ话憬ㄗh入?yún)⒌膫€數(shù)不要超過5個,如果再多就需要考慮封裝對象來傳遞參數(shù)。
以上代碼風(fēng)格如果是團(tuán)隊合作,最好是保持一致。更多代碼規(guī)范 推薦《代碼整潔之道》和《阿里巴巴開發(fā)手冊》
4. 快速短路
如下的偽代碼,當(dāng) user == null時,可以通過 return快速短路返回,去除多余的 esle 語句。因此,在日常業(yè)務(wù)開發(fā)中,遇到類似的情況,一定要做到快速短路,切莫使用大量嵌套。比如 if-esle 語句可以使用 return 快速短路;對于 for 循環(huán)可以使用 break,continue,return等提前跳出,減少不必要的遍歷;
public User getUser(String id) {
// get by id
User user = getById(id);
if(user == null){
return null;
} else {
// else 邏輯
}
// 優(yōu)化成
if(user == null){
return null;
}
// else 邏輯
5. SOLID原則
在類或者方法的設(shè)計上要盡量遵守 SOLID原則,SOLID原則是業(yè)內(nèi)一個比較的原則,關(guān)于 SOLID原則可以參考小編以往的文章:優(yōu)雅代碼,從 SOLID 開始
6. 設(shè)計模式
設(shè)計模式在代碼中的使用是一個比較高階的能力體現(xiàn),巧用設(shè)計模式,可以大大提高代碼的可維護(hù)性、可擴(kuò)展性和可重用性等功能,在很多開源的框架上都有設(shè)計模式的體現(xiàn),比如:Spring 中的單例模式(Single Pattern)工廠模式(Factory Pattern),代理模式(Proxy Pattern),適配器模式(Adapter Pattern),模板模式(Template Pattern),觀察者模式(Observer Pattern)等等。
如果你對設(shè)計模式不是很熟悉,推薦書籍 Erich Gamma 的《設(shè)計模式》,另外,JDK 和 Spring框架也是一個很好的學(xué)習(xí)資料,可以對照著源碼中設(shè)計模式的代碼,加強(qiáng)對設(shè)計模式的理解,然后可以嘗試著在日常開發(fā)中去使用某些設(shè)計模式。
7. 單元測試
單元測試是開發(fā)人員保證代碼質(zhì)量最直接的方式,但現(xiàn)實工作中卻被很多人忽略,有的借故業(yè)務(wù)開發(fā)忙,沒有把時間寫單測,因此把測試功能全部丟給測試人員,有的是不如何寫單測,關(guān)于單元測試,可以參閱小編以往文章:TDD,什么是 測試驅(qū)動開發(fā)?
8. 數(shù)據(jù)結(jié)構(gòu)
有人說 程序 = 數(shù)據(jù)結(jié)構(gòu) + 算法,足以看出數(shù)據(jù)結(jié)構(gòu)和算法的重要性,作為 Java程序員,我們經(jīng)常使用的 HashMap, List, Set, String 等都是 Java 語言對數(shù)據(jù)結(jié)構(gòu)的一種友好封裝,關(guān)于數(shù)據(jù)結(jié)構(gòu),可以參閱小編以往文章:數(shù)據(jù)庫,你必須掌握的8種數(shù)據(jù)結(jié)構(gòu)! 數(shù)據(jù)結(jié)構(gòu)之美:為什么 MySQL 選擇 B+樹做索引?
在實際工作中,很多人面對的工作絕大多數(shù)是業(yè)務(wù)開發(fā),所以處理數(shù)據(jù)肯定離不開數(shù)據(jù)結(jié)構(gòu),建議平時多研究 JDK 的源碼,比如:Map,List 等常用的數(shù)據(jù)結(jié)構(gòu)的底層實現(xiàn)原理,這樣在使用時才能得心應(yīng)手應(yīng)手。
9. 算法
近些年,算法在技術(shù)面試中的比重越來越大,特別是在一些知名的互聯(lián)網(wǎng)公司面試,如果代碼中能夠合理地使用算法,將事半功倍。需要說明的是,在日常的業(yè)務(wù)開發(fā)中,手寫算法的概率比較低,一般會使用三方框架,比如:Google Guava中的限流算法。
對于算法,似乎沒有很好的捷徑,小編的建議是:經(jīng)常去算法網(wǎng)站上刷題,一方面讓算法能力處于隨時可以面試的狀態(tài),一方面通過大量的算法實現(xiàn)量變到質(zhì)變,掌握算法的精髓。
10. 重構(gòu)
重構(gòu)是代碼迭代中一直會存在的行為,當(dāng)發(fā)現(xiàn)代碼有 Bad Smell “壞味道”時,得考慮代碼是否需要重構(gòu)。重構(gòu),一般分為小重構(gòu)和大重構(gòu),小重構(gòu)通常是指工作量小,時間可控(比如時間不超過 2個工作日),甚至不需要測試參與。大重構(gòu)是指時間周期比較大,對代碼的改造比較大,一般需要整體設(shè)計,然后分階段進(jìn)行。關(guān)于代碼重構(gòu),可以參考微服務(wù)作者 Martinfowler 的博客:https://martinfowler.com/books/refactoring.html 或者 Martinfowler 的書籍:《重構(gòu),改善既有代碼的設(shè)計》第二版,《重構(gòu)與模式》《修改代碼的藝術(shù)》
三、總結(jié)
本文分析了高質(zhì)量代碼的幾個衡量維度:
- 可讀性
- 可維護(hù)性
- 可擴(kuò)展性
- 可復(fù)用性
- 可測試性
在編寫代碼時,應(yīng)該多關(guān)注上面幾個因素,一開始可以刻意練習(xí),然后慢慢變成一種習(xí)慣,這樣編寫的代碼質(zhì)量應(yīng)該不會太差。
接著我們介紹了提升代碼質(zhì)量的 10種常用方法:
- 規(guī)范命名
- 巧用注釋
- 代碼風(fēng)格
- 快速短路
- 單元測試
- SOLID原則
- 設(shè)計模式
- 數(shù)據(jù)結(jié)構(gòu)
- 算法
- 重構(gòu)
前 4種方法更多體現(xiàn)在代碼的“形”上,體現(xiàn)了程序員對代碼細(xì)節(jié)的把握,而后 6種方法則更多體現(xiàn)了代碼的“神”,體現(xiàn)了程序員基本功和能力。
根據(jù)小編多年的工作經(jīng)驗,前 4種方法是最簡單,提升代碼質(zhì)量最見效的方法,它們幾乎和能力和工作年限無關(guān),完全在于程序員能否扣住細(xì)節(jié),如果能夠在這 4個方法上多花點(diǎn)功夫,編寫的代碼已經(jīng)可以甩很多人一條街了。
對于后 6種方法,是開發(fā)人員內(nèi)功的體現(xiàn),它要求的是一個長期的積累和修煉過程,是開發(fā)人員能力差距的最好體現(xiàn),所以,通過這 6點(diǎn)可以很好地定位某個程序員的編程段位。建議平時多閱讀一些業(yè)內(nèi)大牛的經(jīng)典書籍。
四、個人心得
工作中,經(jīng)常會遇見一些工作 5年的程序員,寫的代碼質(zhì)量卻遠(yuǎn)不如 3年的程序員,為什么?小編覺得有以下幾個原因:
1. 職業(yè)規(guī)劃不清晰
對于他們來說,大部分都沒有清晰職業(yè)規(guī)劃,沒有方向自然就沒有努力的目標(biāo),沒有目標(biāo)就很容進(jìn)入日復(fù)一日循環(huán)重復(fù)工作的僵局。
2. 代碼能用就行
工作中小編也和一些人討論過,他們對代碼的態(tài)度是:沒有必要這么苛刻,能用就行。如果是剛進(jìn)入社會工作,或許還能理解,如果已經(jīng)工作 3年以上,還是這種想法,競爭力在哪里?作為程序員,代碼質(zhì)量不就是最好的武器嗎?
3. 編程不是興趣
對于很多人來說,選擇編程并不是因為喜歡而是覺得它的工資相對其他行業(yè)來說會高一些。眾所周知,興趣是最好的老師,如果長年累月的干一件沒有興趣的事,能干好嗎?
4. 外部誘惑太多
游戲,短視頻等外部誘惑太多,一玩停不下來,工作之余的大部分時間都被這些“精神鴉片”消耗殆盡。
因為篇幅有限,不可能把每個細(xì)節(jié)點(diǎn)都講清楚,分享一個小編多年來一直在踐行的學(xué)習(xí)方法:持續(xù)學(xué)習(xí),多聽,多看,catch 他人優(yōu)秀的 idea,然后不斷地豐富和領(lǐng)會該 idea,最終形成自己的方法論。豐富和領(lǐng)會的過程就在不斷地強(qiáng)大自己,相信長年累月的積累,終有一天你也可以寫出讓人賞心悅目的高質(zhì)量代碼。