コンポジション
オブジェクトを組み合わせて機能を構築する設計手法で、継承よりも柔軟性が高い
設計オブジェクト指向
コンポジションとは
コンポジション (Composition) は、オブジェクトを組み合わせて機能を構築する設計手法である。「継承よりコンポジションを優先せよ」(Favor composition over inheritance) は GoF デザインパターンの基本原則。
継承 vs コンポジション
| 観点 | 継承 | コンポジション |
|---|---|---|
| 関係 | is-a (犬は動物) | has-a (車はエンジンを持つ) |
| 結合度 | 高い (親の変更が子に波及) | 低い (部品を差し替え可能) |
| 柔軟性 | 低い (単一継承の制約) | 高い (複数の部品を組み合わせ) |
| テスト | 親クラスに依存 | 部品を個別にテスト可能 |
継承の問題
// ❌ 継承: ダイヤモンド問題、脆い基底クラス
class Animal { move() {} }
class Bird extends Animal { fly() {} }
class Penguin extends Bird { fly() { throw new Error('飛べない'); } }
// Penguin は Bird だが fly() できない → リスコフの置換原則に違反
コンポジションで解決
// ✅ コンポジション: 機能を部品として組み合わせ
type Movable = { move: () => void };
type Swimmable = { swim: () => void };
const createPenguin = (): Movable & Swimmable => ({
move: () => console.log('歩く'),
swim: () => console.log('泳ぐ'),
});
// fly を持たない → 型レベルで安全
React でのコンポジション
// ❌ 継承ベース (React では非推奨)
class SpecialButton extends Button { ... }
// ✅ コンポジション: children, render props, hooks
function Card({ children }: { children: React.ReactNode }) {
return <div className="card">{children}</div>;
}
<Card>
<h2>タイトル</h2>
<p>本文</p>
</Card>
関数コンポジション
const double = (x: number) => x * 2;
const addOne = (x: number) => x + 1;
// 関数を合成
const doubleThenAddOne = (x: number) => addOne(double(x));
doubleThenAddOne(3); // 7
// pipe で読みやすく
const pipe = (...fns: Function[]) => (x: any) =>
fns.reduce((v, f) => f(v), x);
const transform = pipe(double, addOne);
Step Functions で小さな Lambda を組み合わせるのはコンポジションの実践。
実践的な知識は関連書籍でも得られる。