2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

実装レベルでのOOPとコンポジション:継承 vs 委譲 vs Strategy

Posted at

概要

オブジェクト指向において、「コードを再利用する」という言葉は常に 継承(Inheritance) と対で語られる。
だが、継承は誤用されると脆弱で変更に弱い設計となるリスクを孕む。

この課題に対する現代的解法が、コンポジション(Composition)Strategy パターンといった手法である。

本稿では、継承と委譲の違い、Strategyパターンの導入意義、
そして実装レベルでどのように使い分け、選択するべきかを具体的に解説する。


1. 継承とは何か?:構造の再利用

class Animal {
  move() {
    console.log("Moving...")
  }
}

class Dog extends Animal {
  bark() {
    console.log("Woof!")
  }
}
  • 子クラスは親クラスの構造と振る舞いをそのまま持つ
  • しかし、親の変更が子に波及しやすい
    密結合の代表例

2. 委譲とは何か?:責務の移譲

class Engine {
  start() {
    console.log("Engine started")
  }
}

class Car {
  constructor(private engine: Engine) {}

  drive() {
    this.engine.start()
    console.log("Car is moving")
  }
}
  • CarEngine を内部に持ち、振る舞いを委譲
  • コンポーネントを差し替えやすく、柔軟性が高い

3. Strategyパターン:振る舞いを差し替え可能に

interface SortingStrategy {
  sort(data: number[]): number[]
}

class QuickSort implements SortingStrategy {
  sort(data: number[]) {
    return [...data].sort((a, b) => a - b) // 仮にQuickSortとする
  }
}

class BubbleSort implements SortingStrategy {
  sort(data: number[]) {
    // シンプルなバブルソート
    const result = [...data]
    for (let i = 0; i < result.length; i++) {
      for (let j = 0; j < result.length - 1; j++) {
        if (result[j] > result[j + 1]) {
          [result[j], result[j + 1]] = [result[j + 1], result[j]]
        }
      }
    }
    return result
  }
}

class DataProcessor {
  constructor(private strategy: SortingStrategy) {}

  process(data: number[]) {
    return this.strategy.sort(data)
  }
}
  • 実行時に挙動を差し替え可能
  • 継承よりも依存の明示性が高く、テスト・拡張に強い

4. 継承・委譲・Strategyの比較

観点 継承 委譲 Strategy
再利用性 高いが密結合 高い(構成の自由あり) 非常に高い(動的切り替え可能)
柔軟性 低い(構造固定) 中(部分的に交換可能) 高(振る舞いごと差し替え可能)
拡張コスト 高い(親子両方の変更必要) 低い 低い(新しい戦略の追加だけ)
テストのしやすさ 低い(内部依存強い) 中(注入可能) 高い(単体テストしやすい)

5. 設計判断フロー

① 振る舞いを複数切り替える必要がある? → YES → Strategy

② 再利用したいロジックがあるが、親子の関係が弱い? → YES → 委譲

③ 継承先は、基底クラスの契約を厳格に守れる? → YES → 継承可

④ 振る舞いの追加が頻繁に起きる? → YES → 継承より委譲/Strategyが安全

6. 実務におけるパターン

✅ UIコンポーネントにおける振る舞い注入(Reactのprops戦略)

<Button onClick={customClickHandler}>Click</Button>

→ Strategy的構造(挙動を外から注入)


✅ サービスの切り替え(インフラ層)

interface EmailService { send(): void }

class SESMailer implements EmailService { /* ... */ }
class LocalMailer implements EmailService { /* ... */ }

const service = isProd ? new SESMailer() : new LocalMailer()

→ 委譲 or Strategyパターンとして実装


よくある誤解と対策

❌ 継承はDRY(Don't Repeat Yourself)だから正義

→ ✅ DRYのために設計が崩れることもある。共通化は「振る舞いの意味」が等しいときに限る


❌ 委譲はオーバーエンジニアリング

→ ✅ 構造の柔軟性と責務分離を考慮すれば、委譲はむしろ健全


❌ Strategyは難しい・重たい

→ ✅ 小規模な Strategy も十分価値がある
関数の注入 すら Strategy の一種とみなせる


結語

オブジェクト指向において再利用や柔軟性を追求するなら、
**「継承よりも構造の制御を重視すべき」**という原則が必要となる。

  • 継承は強力だが脆い
  • 委譲は柔軟かつ秩序的
  • Strategyは再利用と拡張性を両立するデザイン戦略

設計とは、
“変更の可能性に備えて構造を柔軟に保ち、責務を分離し、拡張可能な形でロジックを分配する技法” である。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?