Refactoring Enums 重構列舉
前陣子接收到很有趣的程式碼,基本上就是一個很大的 enum 有多達 30 個以上的 case,還有一個 4000 行 Factory 類別,Factory 裡面都是依照 enum 做各種處理,當初這類別相當複雜,有些處理會呼叫 Api、有些讀取 DB、每個 case 的步驟也都不相同,但通通都在這個類別處理,導致程式碼快 4000 行。
下面先簡化成三個 case 的 enum
1 | enum CakeType{ |
1 | class CakeFactory{ |
其實可以做一個簡單的重構,聽過一種說法是”所有 switch case 都可以用設計模式來取代!”,
所以只要看到有大量 switch case 的判斷,都可以想想有沒有其他解決方式。
對我來說使用 switch case 有三個小缺點
- 無法讓權責分明,行數很多,此方法或類別處理兩條以上的流程 ( ex. A 流程, B 流程 …. )
- 未來新增 case 時,只要有用到 switch case 就要每個地方都去修正 ( ex. 新增 C 流程 )
- 如果想要抽換 A 流程作法,是很難做到的 ( ex. A 流程改成 A-1 流程或是 A-2 流程 )
重構總共需要三步驟:
抽象化你的 enum case
1
2
3
4protocol Cake{
var price: Double { get }
func getIngredientData() -> IngredientData
}實作多個 Cake
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24struct StrawberryCake: Cake{
var price: Double {
return 100.0
}
func getIngredientData() -> IngredientData {
return IngredientData() // ... 很多步驟省略
}
}
struct CheeseCake: Cake{
var price: Double {
return 85.0
}
func getIngredientData() -> IngredientData {
return IngredientData() // ... 很多步驟省略
}
}
struct ChocolateCake: Cake{
var price: Double {
return 120.0
}
func getIngredientData() -> IngredientData {
return IngredientData() // ... 很多步驟省略
}
}修改 enum,方便可以使用它取得 Cake 的實作,必要時也可以動態修改 Cake 的實作
1
2
3
4
5
6
7
8
9
10
11
12
13enum CakeType{
case strawberryCake
case cheeseCake
case chocolateCake
static var allCake: [CakeType: Cake] = [
.strawberryCake: StrawberryCake(),
.cheeseCake: CheeseCake(),
.chocolateCake: ChocolateCake()
]
func getCake() -> Cake?{
return CakeType.allCake[self]
}
}
使用方式:
1 | print(CakeType.strawberryCake.getCake()?.price) |
結論來講,這種作法已經可以將 30 個以上的 case,分散到 30 個物件(檔案)去實作,找起 Bug 來方便許多;
如果要新增 case 也不用在每個 switch case 補上去,因為已經沒有任何 switch case 了,這種抽象化的做法是非常常見的。
但是當然不是所有狀況都需要加上抽象化,這可能會變成 Over Design,需要對專案和需求做進一步的評估才對。
延伸想法:
之前做過另一種重構 enum case 的方式是使用 static 屬性!
1 | struct Cake{ |
希望這篇文章有幫助到您的開發之路!如果能給我一些按讚支持,我會非常感謝您的鼓勵!祝壞蟲遠離您!
評論