程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 重構:越來越長的 switch ... case 和 if ... else if ... else,switchcase

重構:越來越長的 switch ... case 和 if ... else if ... else,switchcase

編輯:C#入門知識

重構:越來越長的 switch ... case 和 if ... else if ... else,switchcase


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

 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)判斷的條件表達式,我們需要引入面向對象的多態機制。第一種方式:使用子類來替換條件表達式

我們以計算不同 role 的員工薪水的 Employee 類為例,一步步講解重構的過程。原始待重構的類結構如下:

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() 根據員工不同的角色返回當月薪水,當然,此處邏輯僅為示意,勿深究。

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

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 工廠函數。

 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() 方法,應用自己的規則來計算薪水。

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 來取代條件表達式

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

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

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved