【Effective Java 14】複合優於繼承

繼承這裏指的是,extends一個父類
複合其實感覺像是一個包裝類
和設計模式中的Decorator模式一樣嗎?

package CompositionBetterInheritance;

import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;

public class InstrumentedHashSet<E> extends HashSet<E>{
private int addCount = 0;
public InstrumentedHashSet(){}

public InstrumentedHashSet(int initCap, float loadFactor){
super(initCap, loadFactor);
}

@Override
public boolean add(E e){
addCount++;
return super.add(e);
}
@Override
public boolean addAll(Collection<? extends E> c){
addCount += c.size();
return super.addAll(c);
}
public int getAddCount(){
return addCount;
}
public static void main(String[] args) {
InstrumentedHashSet<String> s = new InstrumentedHashSet<String>();
s.addAll(Arrays.asList("1","2","3"));
System.out.println(s.getAddCount());//輸出6,因爲allAll方法中調用了add方法
}
}


package CompositionBetterInheritance;

import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class InstrumentedHashSet2<E> implements Set<E>{
private int addCount = 0;
private final Set<E> s;

public InstrumentedHashSet2(Set<E> s) {
this.s = s;
}


public boolean add(E e){
addCount++;
return s.add(e);
}

public boolean addAll(Collection<? extends E> c){
addCount += c.size();
return s.addAll(c);
}

public int getAddCount(){
return addCount;
}

@Override
public void clear() {
s.clear();
}

@Override
public int size() {
return s.size();
}

@Override
public boolean isEmpty() {
return s.isEmpty();
}

@Override
public boolean contains(Object o) {
return s.contains(o);
}

@Override
public Iterator<E> iterator() {
return s.iterator();
}

@Override
public Object[] toArray() {
return s.toArray();
}

@Override
public <T> T[] toArray(T[] a) {
return (T[]) s.toArray(a);
}

@Override
public boolean remove(Object o) {
return s.remove(o);
}

@Override
public boolean containsAll(Collection<?> c) {
return s.containsAll(c);
}

@Override
public boolean retainAll(Collection<?> c) {
return s.retainAll(c);
}

@Override
public boolean removeAll(Collection<?> c) {
return s.removeAll(c);
}

public static void main(String[] args) {
InstrumentedHashSet2<String> s = new InstrumentedHashSet2<String>(new HashSet<String>());
s.addAll(Arrays.asList("1","2","3"));
System.out.println(s.getAddCount());//這裏輸出3,正確
}
}


從這個例子裏面看出
繼承的方式:InstrumentedHashSet extends HashSet implements Set
擴展的子類方法依賴父類的實現細節,造成這個子類很脆弱。

改進後的InstrumentedHashSet implements Set,並且包含一個私有final Set變量,在這個類中調用被包含實例中已有的方法,返回它的結果,這個過程稱爲轉發(forwarding)。

網上摘抄:
這樣做等同於新建一個類,而這個類的全部作用就是給原來繼承的父類的所有已知方法進行代理。
代理方法是死的,不會隨原父類方法的擴充而改變,他只是調用原父類的方法,所以,當原父類新增方法時這個代理的類是不知道也不會受到影響的。
這樣做也是有缺陷的:
雖然這樣不會因爲父類的擴展和子類發生衝突,但同樣的父類擴展的好處也不會被子類自動繼承,這時需要手動去更新代理類,設想一下,在一個比較複雜的系統中,所有繼承關係都使用這種複合關係,那麼任何非葉級的類增加方法都要在該類的複合類中增加代理方法,代碼維護工作量會增加,而且父類增加方法時無法立即知道新方法與子類可能存在的衝突,而導致了重複的工作。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章