理解重構(gòu)
- 重構(gòu)是一種對軟件內(nèi)部結(jié)構(gòu)的改善,目的是在不改變軟件的可見行為的情況下,使其更易理解,修改成本更低。
- 在保持功能不變的前提下,利用設(shè)計(jì)思想、原則、模式、編程規(guī)范等理論來優(yōu)化代碼,修改設(shè)計(jì)上的不足,提高代碼質(zhì)量。
- 重構(gòu)是避免過渡設(shè)計(jì)的有效手段。在我們維護(hù)代碼的過程中,真正遇到問題的時(shí)候,再對代碼進(jìn)行重構(gòu),能有效避免前期投入太多時(shí)間做過度的設(shè)計(jì)。
- 不要等到代碼爛到一定程度才去重構(gòu),持續(xù)重構(gòu)是一個(gè)良好的習(xí)慣。
單元測試
- 單元測試的測試對象是類或者函數(shù),是代碼層級的測試。
- 代碼的可測試性是評判代碼質(zhì)量的一個(gè)重要標(biāo)準(zhǔn)。
- 寫單元測試的過程本身就是代碼重構(gòu)的過程。
- 單元測試用例實(shí)際上就是用戶用例,反映了代碼的功能和如何使用,有助于幫助其他人快速熟悉相關(guān)的代碼。
- 寫單元測試就是針對代碼設(shè)計(jì)覆蓋各種輸入、異常、邊界條件的測試用例,并將這些測試用例翻譯成代碼的過程。
- 依賴注入是實(shí)現(xiàn)代碼可測試性的最有效的手段。
常見的Anti-Patterns (即測試性不好的代碼)
- 未決行為:代碼的輸出是隨機(jī)或者說不確定的,比如跟時(shí)間、隨機(jī)數(shù)有關(guān)的代碼。
- 全局變量
- 靜態(tài)方法
- 復(fù)雜繼承
- 高耦合代碼
解耦
- 解耦,是保證代碼不至于復(fù)雜到無法控制的有效手段。
- 如何判斷代碼是否需要解耦?
- 修改代碼是否牽一發(fā)而動全身?
- 把模塊間、類間的依賴關(guān)系畫出來,看看依賴關(guān)系圖是否過于復(fù)雜?
- 解耦的手段
- 封裝與抽象:可以有效地隱藏實(shí)現(xiàn)的復(fù)雜性,隔離實(shí)現(xiàn)的易變性,給依賴的模塊提供穩(wěn)定且易用的抽象接口。
- 中間層
- 模塊化:模塊化思想的本質(zhì)就是分而治之。
- 其他設(shè)計(jì)思想和原則
- 單一職責(zé)原則
- 基于接口而非實(shí)現(xiàn)變成
- 依賴注入
- 多用組合少用繼承
- 迪米特法則
編碼規(guī)范
- 命名
- 長度:首先要求準(zhǔn)確達(dá)意,在此基礎(chǔ)上越短越好。
- 利用上下文簡化命名。
- 命名要可讀、可搜索。
- 可讀就是,得讓人能讀出來,比如 eyrie 這種命名,沒人知道怎么讀。
- 可搜索就是,整個(gè)項(xiàng)目有個(gè)命名習(xí)慣,比如,統(tǒng)一使用getxxx,不要這里用getxxx,那里用queryxxx。
- 對接口和抽象類的命名,項(xiàng)目內(nèi)統(tǒng)一一下,比如:接口類都寫成Ixxx。
- 注釋
- 注釋跟命名同等重要。
- 注釋寫什么?
- 目的是為了讓代碼更容易看懂,應(yīng)包含三個(gè)方面:做什么、為什么、怎么做。
- 具體情況具體分析,簡單易懂的函數(shù),沒必要寫“為什么、怎么做”,但對于復(fù)雜的函數(shù),或者一些類來說,注釋起到了總結(jié)性作用、文檔的作用,可以讓閱讀代碼的人通過注釋就能大概了解代碼的實(shí)現(xiàn)思路,對閱讀代碼有幫助。
- 目的是為了讓代碼更容易看懂,應(yīng)包含三個(gè)方面:做什么、為什么、怎么做。
- 注釋是否越多越好?
- 通常類和函數(shù)一定要寫注釋,且酌情詳細(xì)些。函數(shù)內(nèi)部的注釋相對少一些。
- 代碼風(fēng)格
- 類、函數(shù)多大才合適?
- 看感覺。團(tuán)隊(duì)項(xiàng)目約定。
- 一行代碼多長合適?
- 看感覺。團(tuán)隊(duì)項(xiàng)目約定。
- 善用空行分割單元塊。
- 四格縮進(jìn)還是兩格縮進(jìn)?
- 團(tuán)隊(duì)項(xiàng)目約定。反正,不要用tab,因?yàn)椴煌腎DE下,tab的顯示寬度不同。
- 大括號是否要另起一行?
- 團(tuán)隊(duì)項(xiàng)目約定。
- 類中成員的排列順序
- emmm..在現(xiàn)代IDE的加持下,感覺不太所謂呢,如果追求好看就約定一下吧。(這句我說的)
- 類、函數(shù)多大才合適?
- 提高代碼可讀性的一些編程技巧
- 把代碼分割成更小的單元塊。
- 把大塊的復(fù)雜邏輯提煉成類或者函數(shù),屏蔽掉細(xì)節(jié),讓閱讀代碼的人不至于迷失在細(xì)節(jié)中。
- 只有代碼邏輯比較復(fù)雜的時(shí)候才建議提煉。
- 避免函數(shù)參數(shù)過多。
- 處理方法:
- 考慮函數(shù)是否職責(zé)單一,能否拆分成多個(gè)函數(shù)。
- 將函數(shù)的參數(shù)封裝成對象。
- 處理方法:
- 勿用函數(shù)參數(shù)來控制邏輯。
- 明顯未被單一職責(zé)原則和接口隔離原則。
- (我說的)這個(gè)還是具體情況具體分析,沒那么絕對。比如要寫一個(gè)支持篩選獲取列表的接口,篩選條件本身就會影響邏輯,但不太適合拆開,不然得寫一堆函數(shù)。
- 函數(shù)設(shè)計(jì)要職責(zé)單一。
- 移除過深的嵌套層次。
- 代碼嵌套過審?fù)且驗(yàn)閕f-else、switch-case、for循環(huán)過度嵌套導(dǎo)致。
- 如果嵌套超過兩層,就要思考一下能否減少嵌套。嵌套本身就不好理解,嵌套過深,不僅理解起來費(fèi)勁,也會因?yàn)槎啻慰s進(jìn)影響代碼整潔。
- 常見的解決思路:
- 去掉多余的if或else。
- 使用continue、break、return關(guān)鍵字,提前退出嵌套。
- 調(diào)整執(zhí)行順序來減少嵌套。
- 將部分嵌套邏輯封裝成函數(shù)調(diào)用。
- 學(xué)會使用解釋性變量。
- 常量取代魔法數(shù)字。比如:定義一個(gè)PI,替換代碼里的3.1415魔法數(shù)字。
- 使用解釋性變量來解釋復(fù)雜表達(dá)式。
- 把代碼分割成更小的單元塊。
- 統(tǒng)一編碼規(guī)范(最重要)
如何發(fā)現(xiàn)代碼質(zhì)量問題 - 常規(guī) checklist
- 目錄設(shè)置是否合理,模塊劃分是否清晰,代碼結(jié)構(gòu)是否滿足“高內(nèi)聚、松耦合”?
- 是否遵循經(jīng)典的設(shè)計(jì)原則和設(shè)計(jì)思想(SOLID、DRY、KISS、YAGNI、LOD等)?
- 設(shè)計(jì)模式是否應(yīng)用得當(dāng)?是否有過度設(shè)計(jì)?
- 代碼是否容易擴(kuò)展?如果要添加新功能,是否容易實(shí)現(xiàn)?
- 代碼是否可以復(fù)用?是否可以復(fù)用已有的項(xiàng)目代碼或類庫?是否有重復(fù)造輪子?
- 代碼是否容易測試?單元測試是否全面覆蓋各種正常和異常的情況?
- 代碼是否易讀?是否符合編碼規(guī)范(比如命名和注釋是否恰當(dāng)、代碼風(fēng)格是否一致等)?
如何發(fā)現(xiàn)代碼質(zhì)量問題 - 業(yè)務(wù)需求 checklist
- 代碼是否實(shí)現(xiàn)了預(yù)期的業(yè)務(wù)需求?
- 邏輯是否正確?是否處理了各種異常情況?
- 日志打印是否得當(dāng)?是否方便debug排查問題?
- 接口是否易用?是否支持冪等、事務(wù)等?
- 代碼是否存在并發(fā)問題?是否線程安全?
- 性能是否有優(yōu)化空間,比如,SQL、算法是否可以優(yōu)化?
- 是否有安全漏洞?比如,輸入輸出校驗(yàn)是否全面?