改善代碼設計 —— 優化物件之間的特性(Moving Features Between Objects)
系列博客
1. 改善代碼設計 —— 優化函數的構成(Composing Methods)
2. 改善代碼設計 —— 優化物件之間的特性(Moving Features Between Objects)
3. 改善代碼設計 —— 組織好你的數據(Composing Data)
4. 改善代碼設計 —— 簡化條件表達式(Simplifying Conditional Expressions)
5. 改善代碼設計 —— 簡化函數調用(Making Method Calls Simpler)
6. 改善代碼設計 —— 處理概括關系(Dealing with Generalization)
1. Move Method (函數搬家)
解釋:
如果 ClassA 的某個函數對 ClassB 有過多的依賴, 可以考慮將這個函數搬到 ClassB 中, 在 ClassA 的這個函數中直接調用 ClassB中這個函數的返回值.
這樣做的好處是減少物件與物件之間的耦合度, 很多情況下這樣做更利于進一步的重構.
沖動前:
{
private double baseSalary = 15000.0;
public double Salary(Employee employee)
{
return baseSalary + 10000 / employee.Level;
}
// other method with baseSalary
}
class Employee
{
public int Level { get; set; }
}
沖動后:
{
private double baseSalary = 15000.0;
public double Salary(Employee employee)
{
return employee.Salary(baseSalary);
}
// other method with baseSalary
}
class Employee
{
public int Level { get; set; }
public double Salary(double baseSalary)
{
return baseSalary + 10000 / Level;
}
}
2. Move Field (值域搬家)
解釋:
有一天發現公司原來計算員工工資的方法不合適了, 比如不是所有的員工起薪 (baseSalary) 都是一萬五, 我想把 baseSalary 搬到 Employee 這個物件中作為員工的一個屬性.
這樣做可使程序擴展性變得更好, 最明顯的是我可以設置不同員工的起薪了.
沖動前:
{
private double baseSalary = 15000.0;
public double Salary()
{
double salary = baseSalary;
//do some compution with salary
return salary;
}
}
沖動后:
{
public double Salary(Employee employee)
{
double salary = employee.BaseSalary;
//do some compution with salary
return salary;
}
}
class Employee
{
public double BaseSalary { get; set; }
}
3. Extract Class (提煉類)
解釋:
當某個物件做的事情過多, 這樣的物件往往含有大量的字段, 屬性和方法. 應該由兩個或更多個物件來分擔這些責任, 這時需要使用 Extract Class.
沖動前:
{
public double BaseSalary { get; set; }
public double Level { get; set; }
public double Salary()
{
double salary = BaseSalary;
//do some complex compution with salary
return salary;
}
}
沖動后:
{
public double Salary(Employee employee)
{
double salary = employee.BaseSalary;
//do some complex compution with salary
return salary;
}
}
class Employee
{
public double BaseSalary { get; set; }
public double Level { get; set; }
public double Salary()
{
EmployeeSalary salary = new EmployeeSalary();
return salary.Salary(this);
}
}
4. Inline Class (將類內聯)
解釋:
Inline Class 和 Extract Class 正好相反. 當一個物件沒有做它應該做的事情, 還專門使用了另一個物件來協助它完成這個職責, 這時可以考慮使用 Inline Class.
如上面所示的例子, 如果我覺得 Employee 這個物件本身就應該實現 Salary 的計算工作, 而不是專門寫一個 Salary 的計算物件來幫助它計算, 可以使用 Inline Class 將 Salary 內聯到 Employee 中去, 也就是"沖動后"的代碼重構成"沖動前"代碼的樣子.
5. Hide Delegate (隱藏委托關系)
解釋:
試想這么一個情況: 有一個 Employee 類, 這個類中含有一個部門 (Department) 屬性, 并且 Department 是一種類. 如果我想知道某職工所在部門的經理人是誰的時候, 我需要通過 xxEmployee.Department.Manger 來訪問. 但這樣做有個缺點是對于其它代碼, Department 是 public 的, 其它代碼能夠訪問到 Department 里的其它特性. 可以在 Employee 類中寫一個 GetManger() 方法進行封裝, 在調用的時候只需要xxEmployee.GetManger() 就行了.
沖動前:
{
public string Manger { get; set; }
}
class Employee
{
public Department Department { get; set; }
}
沖動后:
{
public string Manger { get; set; }
}
class Employee
{
private Department Department;
public string GetManger()
{
return Department.Manger;
}
}
6. Remove Middle Man (干掉中間人)
解釋:
這一條與上一條 Hide Delegate 是相反的. 當我們要訪問 Department 的其它很多特性時, 我們用 Hide Delegate 寫了一條條簡單的委托訪問函數, 當這些函數多到幾乎訪問遍了 Department 里的內容, 可以考慮使用 Remove Middle Man 方法將這些訪問函數干掉.
如上面的例子, 就是將"沖動后"的代碼重構成"沖動前"代碼的樣子.
7. Introduce Foreign Method (引入外加函數)
解釋:
Introduce Foreign Method 有很深的 C#3.0 中擴展方法的味道, 但擴展方法比 Introduce Foreign Method 好在: 擴展方法就好象是被擴展的那個類型自己的方法一樣, 而 Introduce Foreign Method 的函數還需要傳遞這個類型的參數, 但其實編譯器編譯擴展方法后還是會把參數傳進去的, 擴展方法只是一種語法糖.
它的主要目的是實現被調用的類中沒有實現的功能, 注意在進行本項重構時, 如果引入一個外加函數, 這說明這個函數本應該在被調用的類中實現. 下面舉一個簡單到不能再簡單的例子, 這個例子只是說明怎么使用 Introduce Foreign Method, 我并不是說 Int32類型就應該有一個 NextNum 的方法 , 并且實際中多數情況下這種重構用于引用類型
沖動前:
//I want to get num's next
int nextNum = num + 1;
沖動后:
int nextNum = NextNum(num);
private static int NextNum(int arg)
{
return arg + 1;
}
8. Introduce Local Extension (引入本地擴展)
解釋:
如果我不想使用 Introduce Foreign Method, 我覺得它就本來應該實現某某功能, 如果被調用的類不是密封 (sealed) 的話, 可以自定義一個數據類型, 繼承這個類, 在自己定義的數據類型中實現我想要它實現的功能, 這就是 Introduce Local Extension.