# Code Smell (bad semll) - 閱讀筆記
###### tags: `程式理論`
[原文連結](https://sourcemaking.com/refactoring/smells)
[TOC]
## Primitive Obsession (基本型別偏執)
* [連結](https://sourcemaking.com/refactoring/smells/primitive-obsession)
* 解釋:
* 使用基本型別而不是 small object,像 currency, ranges...
* 一串 array 用 index 去區分不同的 field
* 解決方法:
* 邏輯性的將一些資料組建成一個 class,最好可以把此物件相關的方法也搬入 class 內
資料本身有自己的 method 就可以建 class 了,class 可以只有一個資料
* 用常數表示 typecode 顯示可以進行 refactor
將這些 typecode 變成一個 enum
若這些 typecode 會影響程式行為,則可以抽 subclasses,用繼承或 interface
## Long Parameter List (過長參數列)
* [連結](https://sourcemaking.com/refactoring/smells/long-parameter-list)
* 解釋:
* 方法參數超過三到四個
* 解決方法:
* 若參數是其他方法的結果,可以直接在方法內產生即可,這樣就可以減少參數,稱為 **Replace Parameter with Method Call**
* 若多個參數來自同一 object,pass object 就好
* 可以考慮將有關連的參數變成一個 object,這樣就可以 pass object
* 影響:
* 減少了方法的彈性,可以用 interface 增加其彈性
## Data Clumps (資料團)(no data)
* [連結](https://sourcemaking.com/refactoring/smells/data-clumps)
* 解釋:
* 解決方法:
## Switch Statements (Switch敘述)
* [連結](https://sourcemaking.com/refactoring/smells/switch-statements)
* 解釋:可能造成同類型的 switch 分散在各地方。或者不適合用 if 或 switch 的地方
* 解決方法:
* 有 typeCode 的 switch,將他們拆成 subclass
* 若 call 同一個 method,但參數代表的是不同意義,那應該將這些方法各自拆開

* 若有判斷 null,則給 null 一個 default object,顯示當 null 時應該做什麼事,則行為就可以正常執行

## Temporary Field (暫時欄位)
* [連結](https://sourcemaking.com/refactoring/smells/temporary-field)
* 解釋: class 中,在特定情況下才會有值的 field。
可能出現原因是因為某個算法需要很多參數,抽成 field 就不需要傳送,但更好的做法應該是將這些參數抽成物件傳遞。
* 解決方法:
## Refused Bequest
* [連結](https://sourcemaking.com/refactoring/smells/refused-bequest)
* 解釋: 繼承關係中,子class 只繼承部分父class 方法
* 解決方法:
* Replace Inheritance with Delegation: 取消繼承關係,子class method 呼叫父class method
## Divergent Change && Shotgun Surgery (發散式修改 & 霰彈式修改)
* [連結](https://sourcemaking.com/refactoring/smells/divergent-change)
* 解釋: Divergent Change 類似 Shotgun Surgery,兩者差別在於 Divergent Change 是一個改動需同時動到同一個class 其他 method,Shotgun Surgery 則是一個需求在不同 class 都需進行改動。
這兩種 code small 是互相影響的,若 Divergent Change 處理得太過就會變成 Shotgun Surgery。
如 dog 這個 class 代表所有狗,那加一新品種犬類,就需要改外型、出生地...的描述。就有一點像Divergent Change。
但若每一種狗有自己的 class,那如果現在要多加看門這行為,就需要每個 class 都進行改動。就有一點像 Shotgun Surgery。
DB connection string 散佈在各個 class 中,也是一種 Shotgun Surgery 的表現。
* 解決方法:
* Shotgun Surgery 解決方法:將 duplicated code extract method,再將重複的 Method or Field 移至適合的類別。
* 補充:[Divergent Change](http://teddy-chen-tw.blogspot.com/2014/04/3long-parameter-list-divergent-change.html)、 [Shotgun Surgery](http://teddy-chen-tw.blogspot.com/2014/04/4shotgun-surgery-feature-envy.html)
## Parallel Inheritance Hierarchies (平行繼承體系)
* [連結](https://sourcemaking.com/refactoring/smells/parallel-inheritance-hierarchies)
* 解釋:在一個 clasd 加 subClass,必須在另一個 class 也加 subClass
* 解決方法:
* 有介面時,物件同時實作兩個介面或把這兩個介面合在一起
* 補充: [有界面時](https://www.jyt0532.com/2020/04/13/parallel-inheritance-hierarchies/)、[沒界面時](http://teddy-chen-tw.blogspot.com/2014/04/6switch-statements-parallel-inheritance.html)
## Data Class (資料物件)
* [連結](https://sourcemaking.com/refactoring/smells/data-class)
* 解釋: class 內只有資料沒有方法。可能發生原因是職責散落在其他地方,沒做好封裝。
* 解決方法:
* field 用 get() set() 寫入存取
* 集合:讀取時不要直接傳遞原物件,因為可能會被其他人變更資料。寫入時可以開出相對應的方法。
* 若class作為與外部供通的物件,則單純只是 data class 也沒關係。若要與外部互動也不適合用 constructor 注入。
## Feature Envy (依戀情節)
* [連結](https://sourcemaking.com/refactoring/smells/feature-envy)
* 解釋: method 使用其他物件的資料多過自己物件。
* 解決方法:
* 將 method 移去適合的 class
* 若只有部分,可用 extract method 將該部份分離。
* 若 method 使用到多個 class 的資料,判斷比較適合哪一個移去那,其他用物件傳遞的方式傳入。或將方法拆成多個,每部份都移去自己適合的地方。
## Inappropriate Intimacy ()
* [連結](https://sourcemaking.com/refactoring/smells/inappropriate-intimacy)
* 解釋: 兩個類別互動太過緊密
* 解決方法:
* 若 B class 某 field or method 只有 A 在使用,那就直接放到 A 那
* 盡量不要有雙向互動的關係,若一定需要 A 的東西才能訪問 B,那也可以在 A 建一個方法直接 call B,而不需再透過 client 端
* 若 A class 委派了 B 全部的 public,那就直接用繼承吧
## Message Chains (訊息鏈)
* [連結](https://sourcemaking.com/refactoring/smells/message-chains)
* 解釋:看到 a.getB().getC().doSomething(),超過四層就表示有這壞味道,這樣只要 A,B 架構有變所有如此呼叫的地方都需變動。
* 解決方法: 把 getC().doSomething() 抽成一個方法 cDoSomething() 放在 B class 中,也就是 extract method and move class。
## Middle Man ()
* [連結](https://sourcemaking.com/refactoring/smells/middle-man)
* 解釋: 若 class 中有超過一半的方法都是我派別人做事,那就可能存在這種壞味道。可能發生原因是 Message Chains 消除過度,或類別中主要方法已經被搬到他處。Design pattern 中 proxy 或 decorator 也會造成此種現象,但這是可以被忽略的。
* 解決方法: 刪掉這個 class 把職責還給適合的類別。
## Incomplete Library Class (不完美的程式庫類別)
* [連結](https://sourcemaking.com/refactoring/smells/incomplete-library-class)
* 解釋: 所使用中的 library class 中沒有符合需求的方法。可能造成原因為作者已不維護或者是自己需要的特殊需求。
* 解決方法:
* 在頻繁使用的地方自己寫一個需要的方法,大家都使用該方法,[如此做法](https://sourcemaking.com/refactoring/introduce-foreign-method)
* 為該 library 建一個 extension method