前言:本文只摘要了24種代碼「壞味道」最核心的要點(diǎn),也不準(zhǔn)備貼代碼示例,是因?yàn)镾heldon自己在寫的過程中,自然就想起了很多自己寫的代碼「壞味道」。所以希望大家在看文章的過程中多回憶自己的代碼,而不是被冗長的文字和大量代碼示例分散注意力。
1. 神秘命名
命名是編程中最難的兩件事之一。正因?yàn)槿绱?,修改命名可能是最常用的重?gòu)方法。
如果你發(fā)現(xiàn)改名很難,那就說明代碼設(shè)計(jì)有問題。
當(dāng)我們不能給一個(gè)模塊,一個(gè)對(duì)象,一個(gè)函數(shù),甚至一個(gè)變量找到合適名稱的時(shí)候,往往說明我們對(duì)問題的理解還不夠透徹,需要重新去挖掘問題的本質(zhì),對(duì)問題域進(jìn)行重新分析和抽象。
2. 重復(fù)代碼
同一類的兩個(gè)函數(shù)含有相同的表達(dá)式,就應(yīng)該提煉。
3. 過長函數(shù)
活得最長,最好的程序,其中函數(shù)一般都很短。
如果你覺得需要寫注釋,大部分情況就代表這個(gè)東西需要寫進(jìn)一個(gè)獨(dú)立的函數(shù)里面,然后根據(jù)用途來命名比較好。
條件表達(dá)式和循環(huán)往往也是提煉函數(shù)的信號(hào)。
4. 過長參數(shù)列表
使用類可以有效的縮短參數(shù)列表。如果多個(gè)函數(shù)有同樣的幾個(gè)參數(shù),引入一個(gè)類就尤為有意義。
5. 全局?jǐn)?shù)據(jù)
全局?jǐn)?shù)據(jù)仍然是最刺鼻的壞味道之一。它的問題是,全局?jǐn)?shù)據(jù)在任何地方都可以被修改。
所以正確的做法是將全局?jǐn)?shù)據(jù)封裝起來,用函數(shù)將其包起來,這樣就知道那些地方修改了它。
有少量的全局?jǐn)?shù)據(jù)或者無妨,但數(shù)量越多,處理難度就會(huì)指數(shù)上升。(良藥與毒藥的區(qū)別在于劑量)
6. 可變數(shù)據(jù)
核心是縮小作用域。
可以通過封裝變量來確保所有數(shù)據(jù)更新操作都通過很少幾個(gè)函數(shù)來進(jìn)行,使其更容易被監(jiān)控。
7. 發(fā)散式變化
發(fā)散式變化指某個(gè)模塊因?yàn)椴煌脑蛟诓煌姆较蛏习l(fā)生變化。
每次只關(guān)心一個(gè)上下文。
找到引起發(fā)散式變化的原因,將它拆分出來。
8. 霰彈式修改
在每次修改的時(shí)候,應(yīng)該只修改一處,而不是到處的修改。因?yàn)橐粋€(gè)需求,需要修改3處代碼,那么這就需要思考,這3處代碼是否應(yīng)該抽離出來。
一個(gè)常用的策略就是使用內(nèi)聯(lián)(inline)重構(gòu)代碼把本不該分散的邏輯拽回一處。
9. 依戀情結(jié)
模塊化,力求代碼分出區(qū)域,最大化區(qū)域內(nèi)部交互,最小化區(qū)域間交互。
如果兩個(gè)模塊交互頻繁,它們應(yīng)該合并在一起。
10. 數(shù)據(jù)泥團(tuán)
如果在多個(gè)類中,出現(xiàn)了很多相同項(xiàng)的數(shù)據(jù),你需要想想是否要通過將數(shù)據(jù)提煉成類,來抽離出一個(gè)獨(dú)立對(duì)象。
建議新建類而非簡單的結(jié)構(gòu)體。
11. 基本類型偏執(zhí)
很多程序員不愿意創(chuàng)建對(duì)自己的問題域有用的基本類型,如錢,坐標(biāo),范圍等。
比如有程序員用字符串來表示電話號(hào)碼,實(shí)際上你應(yīng)該抽象出來一個(gè)電話號(hào)碼對(duì)象。
12. 重復(fù)的switch
盡量使用多態(tài)而非switch。
13. 循環(huán)語句
我們應(yīng)該用管道操作(如filter和map)來替代循環(huán),這樣能更快的看清被處理的元素和處理他們的動(dòng)作。
14. 冗贅的元素
能簡單的代碼,盡量簡單。未來變復(fù)雜的時(shí)候,再去考慮它。
15. 夸夸其談的通用性
同上,能簡單的代碼,盡量簡單。通用性?過早的優(yōu)化是萬惡之源
16. 臨時(shí)字段
臨時(shí)字段指內(nèi)部某個(gè)字段僅為某種特定情況而設(shè)。
臨時(shí)的字段不應(yīng)該存在。你需要給他們搬個(gè)新家,把所有和臨時(shí)變量相關(guān)的代碼搬至那里。
17. 過長的消息鏈
如果你看到用戶向一個(gè)對(duì)象請(qǐng)求另一個(gè)對(duì)象,然后再向后者請(qǐng)求另一個(gè)對(duì)象,然后再請(qǐng)求另一個(gè)對(duì)象,這就是消息鏈。
消息鏈意味著客戶端會(huì)耦合消息鏈的查找過程。應(yīng)該將查找過程獨(dú)立出一個(gè)函數(shù)。
18. 中間人
委托函數(shù)過多時(shí),減少委托,移除中間人,讓調(diào)用者直接訪問目標(biāo)類進(jìn)行操作。
19. 內(nèi)幕交易
減少模塊之間頻繁的數(shù)據(jù)交換,并把這種交換放到明面上。
20. 過大的類
當(dāng)一個(gè)類代碼行數(shù)太多或者功能職責(zé)太多的時(shí)候,拆掉它。
兩種拆分方法:
提取新類,當(dāng)大類的部分行為可以分解為一個(gè)單獨(dú)的組件,則可以使用提取類的方式拆分。
提取子類,當(dāng)大類的部分行為可以以不同的方式實(shí)現(xiàn)或在極少數(shù)情況下使用,則可以使用提取子類方式拆分。
21. 異曲同工的類
兩個(gè)類有著相同的功能,但方法名稱不同。
重命名方法,并去除掉不必要的重復(fù)代碼。
22. 純數(shù)據(jù)類
純數(shù)據(jù)類常常意味著行為被放在了錯(cuò)誤的地方。處理數(shù)據(jù)的行為應(yīng)該從客戶端移至純數(shù)據(jù)類中。
23. 被拒絕的遺贈(zèng)
如果子類復(fù)用了父類的實(shí)現(xiàn),就應(yīng)該支持父類的接口。
24. 注釋
注釋是提示你,這個(gè)地方該重構(gòu)啦。
如果你覺得需要寫注釋的時(shí)候,請(qǐng)先重構(gòu),試著讓所有注釋都變得多余。
