某個子類只使用超類接口中的一部分,或是根本不須要繼承而來的數據。在子類中新建一個字段用以保存超類;調整子類函數,令它改而委託超類;而後去掉二者之間的繼承關係 java
動機:函數
繼承是個好東西,但有時候它並非你要的。你經常會遇到這樣的狀況:一開始繼承了一個類,隨後發現超類中的許多操做並不真正適用於子類。這種狀況下,你所擁有的接口並未真正反映出子類的功能。或者,你可能發現你從超類中繼承了一大堆子類並不須要的數據,抑或你可能發現超類中的某些protected函數對子類並無什麼意義。工具
你能夠選擇容忍,並接受傳統說法:子類能夠只使用超類功能的一部分。但這樣作的結果是:代碼傳達的信息與你的意圖南轅北轍—這是一種混淆,你應該將它去除。測試
若是以委託取代繼承,你能夠更清楚地代表:你只須要受託類的一部分功能。接口中的哪一部分應該被使用,哪一部分應該被忽略,徹底由你主導控制。這樣作的成本則是須要額外寫出委託函數,但這些函數都很是簡單,極少可能出錯。this
作法: spa
1.在子類中新建一個字段,使其引用超類的一個實例,並將它初始化爲this。code
2.修改子類內的全部函數,讓它們再也不使用超類,轉而使用上述那個受託字段。每次修改後,編譯並測試。對象
注意:你不能這樣修改子類中經過super調用超類函數的代碼,不然它們會陷入無限遞歸。這種函數只有在繼承關係被打破後才能修改。繼承
3.去除兩個類之間的繼承關係,新建一個受託類的對象賦給受託字段。遞歸
4.針對客戶端所用的每個超類函數,爲它添加一個簡單的委託函數
5.編譯,測試。
範例:
濫用繼承的一個經典範例就是讓Stack類繼承Vector類---java 1.1的工具庫(java.util)剛好就是這樣作的。不過,做爲範例,咱們只給出一個比較簡單的形式:
class MyStack extends Vector{ public void push(Object element){ insertElementAt(element,0); } public Object pop(){ Object result = firstElement(); removeElementAt(0); return result; } }
,只要看看MyStack的用戶,咱們就會發現,用戶只要它作4件事:push(), pop(),sieze(), 和isEmpty()。後兩個函數式從vector繼承來的。
把這裏的繼承關係改成委託關係。首先,在MyStack中新建一個字段,用以保存受託的Vector對象。一開始,把這個字段初始化爲this,這樣在重構進行過程當中,就能夠同時使用繼承和委託:
private Vector _vector = this;
如今,開始修改MyStack的函數,讓它們使用委託關係。首先從push()開始:
public void push(Object element){ _vector.insertElementAt(element,0); }
此時,編譯並測試,一切都將運轉如常。如今輪到pop();
public Object pop(){ Object result = _vector.firstElement(); _vector.removeElementAt(0); return result; }
修改完全部的子類函數後,咱們能夠打破與超類之間的聯繫了:
class MyStack{ private Vector _vector = new Vector(); }
而後,對於Stack客戶端可能用到的每個Vector函數,都必須在MyStack中添加一個簡單的委託函數:
public int size(){ return _vector.size(); } public boolean isEmpty(){ return _vector.isEmpty(); }
如今,編譯並測試。若是忘記加入某個委託函數,編譯器會告訴咱們。