Effective Java 3e - Item 18: Favor composition over inheritance 組合優於繼承 筆記。
重點如標題,使用組合優先於使用繼承。也就是少用繼承,多用組合。或是說使用繼承前先想想是否改用組合來達到同樣目的。
不應繼承不同package的具體類別(concrete class),換句話說不應繼承具體類別(concrete class),除非被繼承類別與繼承類別在同個package內(前提是每個類別要分類在正確的package)。
繼承意味著子類(subclass)可能依賴父類(superclass)的實作及狀態達到某種功能,若父類往後被修改可能導致子類壞掉。
在不清楚具體類別的實作細節的情況下,在子類覆寫方法中引用父類的方法可能會導致非預期的結果,因為父類方法間可能互相引用(self-use),但子類覆寫時忽略了此效果。
即使繼承時覆寫了父類全部的方法,若父類之後新增了一個方法,而子類沒覆寫該方法,客戶端使用該新方法可能造成子類失去原有的效果。
即使子類不覆寫父類的方法僅添加新方法,若父類往後新增一個方法且方法簽章與子類的新方法剛好相同,則導致父類新方法被意外覆寫的問題。
子類無法封裝父類的方法,父類的方法仍可被客戶端存取。
使用組合(compositition)方式解決上述繼承造成的問題,將原本要被繼承類別的實例做為被繼承類別的屬性來使用,改成包裹類(Wrapper)與被包裹類(Wrappered Class)的關係。如此仍可完整封裝並利用原本要繼承的類別,且原類別即使新增方法也不會影響新類別的功能。
例如既有的具體類別B
如下。
B
public class B {
public void doSomething() {
System.out.println("do something");
}
}
原本透過A
繼承B
擴充新的功能。
A
public class A extends B {
@Override
public void doSomething() {
System.out.println("do first");
super.doSomething();
}
public void doAnother() {
System.out.println("do another");
}
}
改用composition的方式來達成。
A
public class A {
private final B b;
public A (B b) {
this.b = b;
}
public void doSomething() {
System.out.println("do first");
b.doSomething();;
}
public void doAnother() {
System.out.println("do another");
}
}
以上的組合技巧又稱為裝飾者模式(Decorator pattern)。
使用繼承前先思考繼承類是否完全屬於被繼承類。例如以A
繼承B
前先思考是否所有的A
為B
。
繼承會破壞封裝效果,只有真正的子類與父類關係才適用繼承。
若類別沒有被設計為要成為被繼承的對象,應避免繼承。
參考:
沒有留言:
張貼留言