第十一章 處理概括關係
有一批重構手法專門用於處理類的概括關係(generalization,即繼承關係),其中主要是將函數上下移動於繼承體系之中。
1、上移欄位(Pull Up Field)
判斷若干欄位是否重複,唯一的辦法就是觀察函數如何使用它們。如果它們被函數使用的方式很相似,你就可以將它們歸納到超類去。本項重構從兩方面減少重複:首先它去除了重複的資料聲明;其次它使你可以將使用該欄位的行為從子類移至超類,從而去除重複的行為。
2、上移函數(Pull Up Method)
避免重複是很重要的!重複只會成為錯誤的滋生地。無論何時,只要系統之內出現重複,你就會面臨“修改其中一處卻未能修改另一個”的風險。上移函數(Pull Up Method)是一項很自然的重構。
3、建構函式本體上移(Pull Up Constructor Body)
本項重構手法用於去掉建構函式中的重複代碼。
4、下移函數(Pull Down Method)
如果超類(基類)中的某個函數只與部分子類有關,那麼我們應該將該函數從超類(基類)中下移至特定的子類中(即需要這個特性的子類)。當然,如果你想通過基類對象訪問該函數,為了順利進行,你需要在超類(基類)中將該函式宣告為虛函數(virtual),並提供一個預設的實現(或空實現,但不能為純虛函數)。
5、下移欄位(Pull Down Field)
如果超類(基類)中的某個欄位只被部分子類用到,那麼就應該將這個欄位移至哪些需要這個欄位的子類中去。
6、提煉子類(Extract Subclass)
如果類中的某些特性只被某些執行個體用到,這是需要“提煉子類”訊號。建立一個類,將上面所說的那一部分特性移到子類中。
Extract Class 和 Extract Subclass 其實就是“委託”和“繼承”的抉擇。如果一個類承擔了超過一個職責,則應該使用提煉類(Extract Class),將其中的部分責任委託給新提煉的類去完成。如果一個類的有些執行個體用不到它的所有特性,則應該提煉出一個子類(Extract Subclass),並將這部分特性搬移到新提煉的子類中去。
7、提煉超類(Extract Superclass)
提煉超(基)類。如果兩個類有相似特性,那麼應該將這些相似的特性提煉到一個基類中。 重複代碼是系統中最糟糕的東西!如果你在不同地方做同一件事情,一旦需要修改那些動作,你就得平白做更多的修改。
8、提煉介面(Extract Interface)
C++多重繼承的替代方案。
9、摺疊(合并)繼承體系(Collapse Hierarchy)
重構不僅會建立繼承體系,也可能會摺疊(去除)繼承體系。如果我們發現在一個繼承體系中,某個子類並未帶來該有的價值,我們應該果斷移除它。
10、塑造模板函數(Form Template Method)
塑造模板函數其實就是“定義好處理某種事情的具體步驟(或演算法的骨架)”,儘管這些具體步驟的細節可能不同,但這些不同可以通過多態來實現。
11、以委託取代繼承(Replace Inheritance With Delegation)
我們經常糾結於是使用“繼承”還是使用“委託”?通常,如果某個子類只使用超類介面中的一部分,或者根本不需要繼承而來的資料,這種情況使用委託會更合適。
很多時候,你一開始繼承了一個類,隨後你發現基類中的許多操作並不是真正適用於子類。這種情況下,你所擁有的介面並未真正反映出子類的功能。
你可以選擇容忍,因為這種情況通常並不會影響你實現功能。但是,這樣的代碼所傳達的資訊與你的意圖南轅北撤-----這是一種混淆,我們應該將其去除。
如果以委託取代繼承,你可以更清楚的表明你的意圖:你只需要受託類的一部分功能。介面中的哪一部分應該被使用,哪一部分應該被忽略,完全由你主導控制。當然,這樣做也是有成本的,即你需要額外的編寫出委託函數,但這通常不是問題,因為編寫這樣的委託函數極其簡單。
12、以繼承取代委託(Replace Delegation With Inheritance)
物件導向程式設計中有一條很經典的原則是:優先使用委託而不是繼承。這是因為繼承往往意味著緊耦合,而委託通常能帶來松耦合的系統。但是,有時你也會因為過度信任委託而出錯。例如,如果你發現自已需要使用委託類的所有函數,並且費了很大力氣編寫所有極簡的委託函數,那麼你應該實施本項重構。
當然,我們說過:優先使用委託而不是繼承。所以,在使用本項重構之前,有兩條告誡需要牢記於心:
首先,如果你並沒有使用受託類的所有函數,那麼就不要實施本項重構,因為子類應該總是遵循基類的介面(裡氏替換原則)。就算過多的委託函數讓你煩心,你仍然有別的選擇,還記得“移除中間人(Remove Middle Man)”那項重構嗎?你可以直接返回受託對象給用戶端,讓客戶代碼自己去調用需要的函數,從而可以免除你編寫眾多委託函數的麻煩。
其次,如果受託對象被不止一個其他對象共用,而且受託對象是可變的。那麼此時你也應該停止本項重構。資料共用是必須由委託關係承擔的一種責任,你無法把它轉給繼承關係。當然,如果僅僅只是資料共用,而資料對象是不可變的,那麼我們可以複製對象達到資料共用的目的。