第三章 代碼的壞味道
從某種意義上講,理解“何時應該重構”,“何時停止重構”比“知道如何重構”更重要!
作者在這一章裡試圖通過“代碼的壞味道”這樣一種暗喻來解釋“重構的時機和場合”。任何人都沒辦法給出何時必須重構的精確衡量標準。理解並掌握這個標準需要我們的直覺。這也與項目的實際情況有關,例如品質要求,預期的服役年限,安全性要求等等。因此,我們必須培養自己的判斷力,學會判斷一個類內有多少執行個體變數才算太大,一個函數內有多少行代碼才算太長。
1. Duplicated Code(重複代碼)*****
代碼重複是萬惡之源。
如果兩個毫不相關的類出現重複代碼,你應該考慮對其中一個使用提煉函數,將重複代碼提煉到一個獨立的類中,然後在另一個類內使用這個新類。但是,重複代碼所在的函數也可能的確只應該屬於某個類,另一個類只應該調用它,抑或這個函數可能屬於第三個類,而另兩個類應該引用這第三個類。你必須決定這個函數放在哪兒更合適,並確保它被安置後就不會再在其它任何地方出現。這個時候,請忘卻你“放哪兒都可以”的念頭!
2. Long Method(過長的函數)**
一般情況下,函數只是太長並不會導致什麼嚴重的問題。如果一個函數乾著許多不相關聯的任務,那麼這是“職責不單一”的表現,我們就需要使用提煉函數重構它,讓函數內的代碼在同一個任務層級上相互協作。
如果函數職責單一,但是代碼實在太長,那麼不妨考慮用方法對象替換方法(Replace Method With Method Object)重構之。我們經常這樣做,並且收到了不錯的效果。
函數太長難以理解,其實你總有辦法讓它變得短小一些。
3. Large Class (過大的類)****
太多執行個體變數。分離之。
1)有時候類並非在所有時刻都使用所有執行個體變數。這時就應該提煉類或提煉子類重構之。
2)如果類中有太多代碼,通常也就意味著代碼重複。這時也可以用提煉類和提煉子類重構之。
3)如果Large Class類是個GUI類,那麼可以把資料和行為分離到一個獨立的類中。可以使用重複觀察資料方法重構之。
4. Long Parameter List(過長的參數列)**
函數需要的東西多半可以在函數的宿主類中找到。再者,處理“過長的參數列”這種味道通常並不太困難。
5. Divergent Change(發散式變化)***
一個類受多種變化的影響。重構目標:滿足單一職責原則。
6. Shotgun Surgery (霰彈式變化)***
一個變化會引發多個類的修改。
7. Feature Envy(依戀情結)****
函數對某個類的興趣高過對自己所處類的興趣。
8. Data Clumps(資料泥團)****
以類表示概念。
9. Primitive Obsession(基本類型偏執)****
以類表示概念。
10. Switch Statements(switch 驚悚現身)*****
多態是switch語句的天生殺手。
11. Parallel Inheritance Hierarchies(平行繼承體系)****
另一種重複。讓一個繼承體系的執行個體引用另一個繼承體系的執行個體。
12. Lazy Class類)****
冗餘的類就如同冗餘代碼一樣,必須果斷的移除。
13.Speculative Generality(夸夸其談未來型)***
不要告訴別人“萬一我要用它呢?”。
14. Temporary Field(令人迷惑的臨時欄位)****
1)有時會看到這樣一個對象:其內的某個執行個體變數僅為某種特定情況而設。在變數未被使用的情況下猜測其當初的設定目的,會讓人發瘋的。如果遇到這種情況,可以使用提煉類給這個可憐的孤兒創造一個新家,並把與其相關的所有變數和代碼放進這個新家。
2)還有一種情況容易導致這種壞味道的出現:類中有一個複雜演算法需要好幾個變數,為了避免過長參數列,所以經常把這些變數做為類的成員欄位。但實際上這些欄位只在使用該演算法時才有效。這時候就需要利用提煉類把這些變數和相關函數提煉到一個獨立的類中。
在My Code中,經常有這樣的壞味道。
15. Message Chains(過度耦合的訊息鏈)**
隱藏代理,增加中間人。
16. Middle Man(中間人)**
大部分情況下,我們認為委託好於繼承,但也不全是。
17. Inappropriate Intimacy(狎昵關係)***
兩個類過於親密,花費太多時間去探究彼此的private部分。
18. Alternative Classes with Different Interfaces(異曲同工的類)***
提煉超類(基類)。
19. Incomplete Library Class(不完美的庫類)
添加外加函數,引入本地擴充。這一招與其說是重構手法,不如說是編碼技巧。
20. Data Class (幼稚的資料類)
一個類如果只有資料沒有行為,起初他可能只是個孩子,但要通過重構協助他長大----他應該承擔他應該承擔的責任。
21. Refused Bequest(被拒絕的遺贈)
通常,委託優先於繼承。
22. Comments(過多的注釋)
當你感覺需要撰寫注釋時,請先嘗試重構,試著讓所有注釋都變得多餘。即我們應該儘可能撰寫“自解釋代碼”。注釋的用途有兩個:一是可以用來記述將來的打算;二可以用來記錄“為什麼做某某事”。