Composition は他のオブジェクトを保持("has-a"関係)して委譲することで動作を構築します。Inheritance はクラスを拡張("is-a")して動作を習得します。"合成を優先する"という助言が存在するのは、継承は基底クラスへの密結合で壊れやすいのに対し、合成は柔軟に保たれるからです。
なぜ重要なのか
java
<T> <T> {
{ add(x); }
T { remove(size() - ); }
}
Composition は他のオブジェクトを保持("has-a"関係)して委譲することで動作を構築します。Inheritance はクラスを拡張("is-a")して動作を習得します。"合成を優先する"という助言が存在するのは、継承は基底クラスへの密結合で壊れやすいのに対し、合成は柔軟に保たれるからです。
<T> <T> {
{ add(x); }
T { remove(size() - ); }
}
class Stack<T> {
private final List<T> items = new ArrayList<>(); // HAS-A list
void push(T x) { items.add(x); }
T pop() { return items.remove(items.size() - 1); }
// only push/pop are exposed → LIFO invariant is safe
}
であるのではなく保持することで、Stackは必要な操作だけを公開します。
| 合成 | 継承 |
|---|---|
| 柔軟、実行時にパーツを交換可能 | コンパイル時に固定 |
| 内部を隠す(委譲) | インターフェース全体を継承 |
| より多くの配線/ボイラープレート | 真の is-a の場合はコード量が少ない |
| 脆弱な基底クラスを回避 | 基底が変更されるとサブクラスが破綻 |
継承は真正で安定した is-a 関係にのみ使用してください。合成は "この動作が必要"という場面で使用します。
継承は親クラスのインターフェース全体を公開するため、サブクラスは独自の契約を完全に制御できません—Stack extends ArrayListの漏洩は古典的な例です。
合成は関係を明示的で交換可能に保つため、依存関係注入、ストラテジーパターン、そして最も柔軟な設計の多くはこれに依存しています。