改善代碼設計 —— 處理概括關系(Dealing with Generalization)
系列博客
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. Pull Up Field (提升值域)
解釋:
如果發現每個子類都擁有相同的某個值域, 那么使用 Pull Up Field 將這個值域提升到父類中去.
沖動前:
沖動后:
2. Pull Up Method (提升函數)
解釋:
如果每個子類都有相同的某個函數, 這個函數做同樣的事情, 而且結果也相同, 那么使用 Pull Up Method 將這個函數提升到父類中去.
沖動前:
沖動后:
3. Pull Up Constructor Body (提升構造函數)
解釋:
特別要注意每個子類中重復的代碼, 如果可能的話盡量將它們提煉成方法并搬到父類中去. 對于子類的構造函數, 我們需要找出相同的部分, 用這些相同的部分組成父類的構造函數.
如下面的例子, 如果不光 Salesman, 還有 Engineer 等等類別的員工在構造他們的時候都需要 name 和 level 屬性, 可以考慮使用 Pull Up Constructor Body 將設置這兩個屬性提升到父類的構造函數中去.
沖動前:
{
public string Name { get; set; }
public int Level { get; set; }
//...
}
class Salesman : Employee
{
public string Hobby { get; set; }
public Salesman(string name, int level, string hobby)
{
this.Name = name;
this.Level = level;
this.Hobby = hobby;
}
//...
}
//...
沖動后:
{
public string Name { get; set; }
public int Level { get; set; }
public Employee(string name, int level)
{
this.Name = name;
this.Level = level;
}
//...
}
class Salesman : Employee
{
public string Hobby { get; set; }
public Salesman(string name, int level, string hobby):base(name,level)
{
this.Hobby = hobby;
}
//...
}
//...
4. Push Down Method (降低函數)
解釋:
父類里有某個函數只與一部分子類有關, 并不是與所有的子類都有關, 使用 Push Down Method 重構手段將這些函數放到使用它們的子類中去, 而不要放到父類中.
沖動前:
沖動后:
5. Push Down Field (降低值域)
解釋:
與 Push Down Method 描述的問題類似, 父類中如果某個值域并不是對于每個子類都有用的, 應該把它放到需要它的子類中去.
6. Extract Subclass (提煉子類)
解釋:
我們產生了類的一些實例, 但并不是每個實例都用得到類中所有的特性, 往往這是類中功能設計過多的原因造成的. 嘗試從這個類中提煉出一些子類, 子類中的功能應該劃分得很明確.
7. Extract Superclass (提煉父類)
解釋:
如果你發現有兩個類, 他們有很多相同的特性, 嘗試找出兩個類中相同的特性, 如果你能找到一個合適的理由讓這兩個類繼承自一個父類, 從而你可以提煉出這個父類, 父類中包含那兩個類中相同的部分.
8. Extract Interface (提煉接口)
解釋:
類與類之間經常會相互調用, 比如 ClassA 的某個函數里需要 ClassB 里的某個值域或者某個函數的返回值, 因此我將整個 ClassB 作為參數傳遞給 ClassA 的這個函數, 這意味著 ClassA 的這個函數能夠調用 ClassB 里所有的功能, 可不可以給 ClassA 的這個函數劃定一個特定的職能呢? 讓它只能做某些事情, 而避免其它 "越權行為". 有句話常被人說起 —— 使用接口來降低耦合性, 這就是 Extract Interface 的功勞.
這條重構手段經常被使用到, 主要解決類對另一個類的依賴問題, 降低了耦合性.
沖動前:
{
public void Read()
{
//...
}
public void Translate()
{
//...
}
//some other methods
}
class WeatherService
{
public string GetWeather(Xml xml)
{
xml.Read();
xml.Translate();
//other code, but without xml object
}
//some other methods
}
沖動后:
{
void Read();
void Translate();
}
class Xml : IOperation
{
public void Read()
{
//...
}
public void Translate()
{
//...
}
//some other methods
}
class WeatherService
{
public string GetWeather(IOperation operation)
{
operation.Read();
operation.Translate();
//other code, but without xml object
}
//some other methods
}
注意代碼高亮的那行已經變成調用接口, 而不是僅依賴調用 Xml 類的實例, 對于單個類實現這樣的接口并不是很有價值, 如果很多類都實現了同樣的接口, 這將是很有用的事情.
9. Collapse Hierarchy (去掉不必要的繼承關系)
解釋:
龐大的繼承體系很容易變得復雜, 理清父類與子類它們各自的職能是非常重要的, 你很有可能會發現不必要的子類, 那么使用 Pull Up Field 和 Pull Up Method 將它干掉.
10. Replace Inheritance with Delegation (用委派取代繼承)
解釋:
如果你想讓 ClassA 使用某個類 (如 ClassB) 的某個函數, 就讓 ClassA 繼承自 ClassB, 這將是一個多么糟糕的設計! 你可以在 ClassA 中包含一個 ClassB 的值域, 通過這個值域調用你需要的函數, 這個值域就是"委派", 而你這時可以去掉 ClassA 和 ClassB 之間不該存在的繼承關系了.
11. Replace Delegation with Inheritance (用繼承代替委派)
解釋:
這一條與 Replace Inheritance with Delegation 正好相反, 如果你需要使用委派中的所有函數, 這時你就應該想想它們之間是不是存在繼承關系.