物件導向應用-為何選用組合而非繼承?(OOP Composition or Inheritance?)

之前曾經聽過一些人在針對OOP關係依賴時,對於使用繼承頗有微詞,這一派人會主張應該使用組合而非繼承,今天我就來分享組合以及繼承。

Composition Over Inheritance (組合優於繼承)

為甚麼不推薦使用繼承?

  • 確實,繼承解決了重複程式碼的問題。但繼承層次過深、過複雜,也會影響程式碼的可維護性。

假設我們要設計一個關於鳥的類別,我們知道大多數的鳥都會飛,所以我們一開始會這樣寫

1
2
3
public class AbstractBird {
public void fly() {}
}

但鴕鳥呢?鴕鳥可不會飛啊,但鴕鳥也是鳥,除非你這樣寫

1
2
3
4
5
public class Ostrich extends AbstractBird {
public void fly() {
throw new UnsupportMethodException("I can not fly");
}
}

這可以解決問題,但不優雅,因為不會飛的鳥其實不少,比如企鵝。對於所有不會飛的鳥去全部重寫fly()方法毫無疑問地屬於重複程式碼

你可以拆出AbstractFlyableBird與AbstractUnflyableBird,但其他行為呢,生殖呢?下蛋呢?你會寫出無數用來繼承的父類別,而這些程式碼將會難以維護,雖然不重複,但所有實現的程式碼都分散在各個父類別中。

繼承最大的問題就是,繼承層次過深,導致影響了程式碼的可讀性與可維護性


組合(Composition)

  • 把功能拆開,改用介面去套用。
1
2
3
4
5
6
7
8
9
public interface Flyable {
void fly();
}
public interface Tweetable {
void tweet();
}
public class Ostrich implements Tweetable,Flyable {
//透過一個個小介面去組合功能,內容實作還是維持在此類別中。
}

這個做法,相比使用多個父類別來說,程式碼可以集中在同一個類別中,相形之下比較好維護。但其他非鴕鳥的鳥卻還是必須得重新寫一次實作方法,這不是interface的問題,他已經做得很好了。追根究底,因為java沒有辦法支援多重繼承,所以必須要花更多心力去確保程式碼的品質,盡力確保消除程式碼重複。


委託(Delegation)

  • 延續上方的組合,但是實作每一個元件(interface)的內容,並放在要主要使用的類別中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public interface Flyable {
void fly();
}
public FlyAbility implements Flyable {
void fly() { //省略實作
}
}
public interface Tweetable {
void tweet();
}
public TweetAbility implements Tweetable {
void tweet() { //省略實作
}
}
public class Ostrich implements Tweetable,Flyable {
private TweetAbility tweetAbility = new TweetAbility();
private FlyAbility flyAbility = new FlyAbility();

public void fly() {
flyAbility.fly();
}
public void tweet() {
tweetAbility.tweet();
}

}

// 或是使用Java 11的interface default
public interface Flyable {
default void fly() { //預設實現方法
}
}

透過這個方法,你就可以實現DRY原則。程式碼不重複且很好利用,唯一的缺點是感覺有點雞肋。那麼最後我們來提一提──

何時使用繼承、何時使用組合?

  • 如果類別之間的繼承關係結構穩定,不會輕易改變,就用繼承就好。
  • 繼承層級比較淺,比如最多繼承兩層,繼承關係不複雜,就用繼承就好。
  • 如果系統越不穩定,繼承層次很深,繼承關係複雜,就建議使用組合。

💡繼承可以使用的時機,來自在同一個流程中,對繼承同一個父類別的不同類別做一樣的事。(使用多態性)

以上就是我的學習與一些整理,希望這段內容可以幫到與我有相同困惑的學習者。


物件導向應用-為何選用組合而非繼承?(OOP Composition or Inheritance?)
https://clark1945.github.io/2025/11/02/物件導向應用-為何選用組合而非繼承?-OOP-Composition-or-Inheritance/
Author
Clark Liu
Posted on
November 2, 2025
Licensed under