重構:愈來愈長的 switch ... case 和 if ... else if ... else

在代碼中,時常有就一類型碼(Type Code)而展開的如 switch ... case 或 if ... else if ... else 的條件表達式。隨着項目業務邏輯的增長及代碼經年累月的修改,這些條件判斷邏輯每每變得愈來愈冗長。特別是當一樣的邏輯判斷出如今多個地方的時候(結構示意以下),代碼的可讀性和維護難易程度將變得很是的糟糕。每次修改時,你必須找到全部有邏輯分支的地方,並修改它們。ide

 1 switch(type)
 2 {
 3     case "1":
 4         ...
 5         break;
 6     case "2":
 7         ...
 8         break;
 9     case default:
10         ...
11         break;
12 }
13 
14 ... ...
15 ... ...
16 
17 switch(type)
18 {
19     case "1":
20         ...
21         break;
22     case "2":
23         ...
24         break;
25     case default:
26         ...
27         break;
28 }

當代碼中出現這樣狀況的時候,你就應考慮該重構它了(又有一種說法:要極力的重構 switch ... case 語句,避免它在你的代碼中出現)。函數

 

重構掉類型碼(Type Code)判斷的條件表達式,咱們須要引入面向對象的多態機制。第一種方式:使用子類來替換條件表達式this

咱們以計算不一樣 role 的員工薪水的 Employee 類爲例,一步步講解重構的過程。原始待重構的類結構以下:spa

 1 public class Employee_Ori
 2 {
 3     private int _type;
 4 
 5     private const int ENGINEER = 1;
 6     private const int SALESMAN = 2;
 7     private const int MANAGER = 3;
 8 
 9     private decimal _baseSalary = 10000;
10     private decimal _royalty = 100;
11     private decimal _bonus = 400;
12 
13     public Employee_Ori(int type)
14     {
15         this._type = type;
16     }
17 
18     public decimal getEmployeeSalary()
19     {
20         var monthlySalary = 0m;
21 
22         switch (this._type)
23         { 
24             case ENGINEER:
25                 monthlySalary = _baseSalary;
26                 break;
27             case SALESMAN:
28                 monthlySalary = _baseSalary + _royalty;
29                 break;
30             case MANAGER:
31                 monthlySalary = _baseSalary + _bonus;
32                 break;
33         }
34 
35         return monthlySalary;
36     }
37 }

其中方法 getEmployeeSalary() 根據員工不一樣的角色返回當月薪水,固然,此處邏輯僅爲示意,勿深究。設計

用子類方法重構後的類結構是這樣的:code

下面詳細展開。首先,以類型碼(Type Code)的宿主類 Employee 類爲基類,爲每一個角色創建子類。對象

 1 public class Engineer_Sub : Employee_Sub
 2 {
 3     ...
 4 }
 5 
 6 public class Salesman_Sub : Employee_Sub
 7 {
 8     ...
 9 }
10 
11 public class Manager_Sub : Employee_Sub
12 {
13     ...
14 }

同時,將 Employee 基類的構造函數改造爲靜態 Create 工廠函數。blog

 1 public static Employee_Sub Create(int type)
 2 {
 3     Employee_Sub employee = null;
 4 
 5     switch (type)
 6     {
 7         case ENGINEER:
 8             employee = new Engineer_Sub();
 9             break;
10         case SALESMAN:
11             employee = new Salesman_Sub();
12             break;
13         case MANAGER:
14             employee = new Manager_Sub();
15             break;
16     }
17 
18     return employee;
19 }

該靜態工廠方法中也含有一 switch 邏輯。而重構後的代碼中 switch 判斷只有這一處,且只在對象建立的過程當中涉及,不牽扯任何的業務邏輯,因此是能夠接受的。生命週期

每一個角色子類均覆寫基類中的 getEmployeeSalary() 方法,應用本身的規則來計算薪水。ip

1 private decimal _baseSalary = 10000;
2 
3 public override decimal getEmployeeSalary()
4 {
5     return _baseSalary;
6 }

這樣,基類 Employee 中的 getEmployeeSalary() 方法已無實際意義,將其變爲 abstract 方法。

1 public abstract decimal getEmployeeSalary();

子類已經徹底取代了臃腫的 switch 表達式。若是 Engineer, Salesman 或者 Manager 有新的行爲,可在各自的子類中添加方法。或後續有新的 Employee Type, 徹底能夠經過添加新的子類來實現。在這裏,經過多態實現了服務與其使用這的分離。

 

可是,在一些狀況下,如對象類型在生命週期中須要變化(細化到本例,如 Engineer 晉升爲 Manager)或者類型宿主類已經有子類,則使用子類重構的辦法就沒法奏效了。這時應用第二種方法:用 State/Strategy 來取代條件表達式

一樣,先上一張該方法重構後的類結構:

抽出一 EmployeeType 類,類型碼的宿主類 Employee 對其進行引用。

1 public class Employee_State
2 {
3     // employee's type can be changed
4     private EmployeeType_State _employeeType = null;
5 }

EmployeeType 爲基類,具體員工類型做爲其子類。EmployeeType 類中含有一建立員工類型的靜態工廠方法 Create。

 1 public static EmployeeType_State Create(int type)
 2 {
 3     EmployeeType_State empType = null;
 4 
 5     switch (type)
 6     {
 7         case ENGINEER:
 8             empType = new EngineerType_State();
 9             break;
10         case SALESMAN:
11             empType = new SalesmanType_State();
12             break;
13         case MANAGER:
14             empType = new ManagerType_State();
15             break;
16     }
17 
18     return empType;
19 }

將員工薪水的計算方法 getEmployeeSalary() 從 Employee 類遷徙到員工類型基類 EmployeeType 中。各具體員工類型類均 override 該方法。考慮到 EmployeeType 已無具體業務邏輯意義,將 EmployeeType 中的 getEmployeeSalary() 方法改成抽象方法。

1 public class EngineerType_State : EmployeeType_State
2 {
3     private decimal _baseSalary = 10000;
4 
5     public override decimal getEmployeeSalary()
6     {
7         return _baseSalary;
8     }
9 }

 

最後,附上示意代碼,點這裏下載。

 


參考資料:

《重構 改善既有代碼的設計》 Martin Fowler

相關文章
相關標籤/搜索