IoC と DI - 制御の反転と依存性注入
このドキュメントの目的
IoC(制御の反転) と DI(依存性注入) の概念を深く理解し、なぜこれらがモダンなフレームワークの基盤となっているかを学びます。
目次
IoC(制御の反転)とは
「制御」とは何か
まず「制御」が何を指すかを理解する。
┌─────────────────────────────────────────────────────────────┐
│ 「制御」とは? │
├─────────────────────────────────────────────────────────────┤
│ │
│ プログラムにおける「制御」= 処理の流れを決めること │
│ │
│ ・いつ処理を開始するか │
│ ・どの順番で処理するか │
│ ・どのオブジェクトを使うか │
│ ・いつ終了するか │
│ │
└─────────────────────────────────────────────────────────────┘
従来のプログラム: 自分で制御
┌─────────────────────────────────────────────────────────────┐
│ 従来: アプリケーションが制御する │
├─────────────────────────────────────────────────────────────┤
│ │
│ あなたのコード │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 1. main() から始まる │ │
│ │ 2. 必要なオブジェクトを自分で作る │ │
│ │ 3. ライブラリを呼び出す │ │
│ │ 4. 処理の流れを自分で決める │ │
│ │ │ │
│ │ あなた ────呼び出す────→ ライブラリ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ あなたが「主」、ライブラリが「従」 │
│ │
└─────────────────────────────────────────────────────────────┘
IoC: 制御を「反転」させる
┌─────────────────────────────────────────────────────────────┐
│ IoC: フレームワークが制御する │
├─────────────────────────────────────────────────────────────┤
│ │
│ フレームワーク │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 1. フレームワークが起動する │ │
│ │ 2. フレームワークが処理の流れを決める │ │
│ │ 3. 必要なときにあなたのコードを呼び出す │ │
│ │ │ │
│ │ フレームワーク ────呼び出す────→ あなたのコード │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ フレームワークが「主」、あなた のコードが「従」 │
│ → 制御の主従関係が「反転」している │
│ │
└─────────────────────────────────────────────────────────────┘
Hollywood Principle
IoCは「ハリウッドの原則」とも呼ばれる。
┌────────── ───────────────────────────────────────────────────┐
│ "Don't call us, we'll call you." │
│ (こちらから連絡するな、必要なら呼ぶ) │
├─────────────────────────────────────────────────────────────┤
│ │
│ 映画のオーディション: │
│ │
│ 役者「私を使ってください!いつ出番ですか?」 │
│ 監督「こちらから連絡する。待っていろ。」 │
│ │
│ 役者 = あなたのコード(受動的に待つ) │
│ 監督 = フレームワーク(主導権を持つ) │
│ │
│ 役者は「演技する」ことだけに集中できる │
│ いつ・どこで演技するかは監督が決める │
│ │
└─────────────────────────────────────────────────────────────┘
IoCの2つの側面
IoCには大きく2つの側面がある。
┌─────────────────────────────────────────────────────────────┐
│ IoCの2つの側面 │
├─────────────────────────────────────────────────────────────┤
│ │
│ IoC(制御の反転) │
│ │ │
│ ┌───────────────┴───────────────┐ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ 1. 処理フローの │ │ 2. 依存関係の │ │
│ │ 制御の反転 │ │ 制御の反転 │ │
│ │ │ │ │ │
│ │ 誰が誰を呼ぶか │ │ 誰が何を作るか │ │
│ └─────────────────┘ └─────────────────┘ │
│ │
│ フレームワークが オブジェクトの生成・ │
│ あなたを呼ぶ 選択をフレームワークに │
│ (Hollywood Principle) 委ねる │
│ │
└─────────────────────────────────────────────────────────────┘
1. 処理フローの制御の反転
これは先ほど説明した「フレームワークがあなたを呼ぶ」という話。
┌─────────────────────────────────────────────────────────────┐
│ 処理フローの制御の反転 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 【従来】あなた → ライブラリを呼ぶ │
│ 【IoC】 フレームワーク → あなたを呼ぶ │
│ │
│ 実現方法: │
│ ├── コールバック / イベント駆動 │
│ │ 「何かが起きたら、この処理を呼んでね」 │
│ │ │
│ └── テンプレートメソッド │
│ 「骨組みは決まっている。穴だけ埋めてね」 │
│ │
└─────────────────────────────────────────────────────────────┘
2. 依存関係の制御の反転
こちらがDIに繋がる重要な概念。
┌─────────────────────────────────────────────────────────────┐
│ 依存関係の制御の反転 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 「どのオブジェクトを使うか」を誰が決めるか? │
│ │
│ 【従来】 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ UserService が自分で決める │ │
│ │ │ │
│ │ 「俺は PostgresRepository を使う!」 │ │
│ │ │ │
│ │ → 自分で生成、自分で選択 │ │
│ │ → 変更するにはコードを書き換える必要がある │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 【IoC】 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ UserService は決めない(外部に委ねる) │ │
│ │ │ │
│ │ 「Repository が欲しい。何でもいい。」 │ │
│ │ │ │
│ │ → 外部(フレームワーク等)が適切なものを渡す │ │
│ │ → 変更は設定だけで可能 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ この「依存関係の制御の反転」を実現するのが DI │
│ │
└─────────────────────────────────────────────────────────────┘
なぜ「反転」なのか
┌─────────────────────────────────────────────────────────────┐
│ 依存関係の制御が「反転」するイメージ │
├─────────────────────────────────────────────────────────────┤
│ │
│ 【従来: 自分で制御】 │
│ │
│ UserService │
│ │ │
│ │ 「俺が決める!俺 が作る!」 │
│ ▼ │
│ new PostgresRepository() │
│ │
│ 制御の方向: UserService → Repository │
│ │
├─────────────────────────────────────────────────────────────┤
│ │
│ 【IoC: 外部が制御】 │
│ │
│ 外部(フレームワーク/組み立て役) │
│ │ │
│ │ 「これを使いなさい」 │
│ ▼ │
│ UserService ← Repository を受け取る │
│ │
│ 制御の方向: 外部 → UserService │
│ (従来と逆向き = 反転) │
│ │
└─────────────────────────────────────────────────────────────┘
依存関係と密結合の問題
DIを理解する前に、「依存関係」と「密結合」の問題を理解する。
依存関係とは
┌─────────────────────────────────────────────────────────────┐
│ 依存関係の例 │
├─────────────────────────────────────────────────────────────┤
│ │
│ UserService が UserRepository を使っている │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ UserService │───────→│ UserRepository │ │
│ │ │ 依存 │ │ │
│ └──────────────────┘ └──────────────────┘ │
│ │
│ UserService は UserRepository なしでは動作できない │
│ → UserService は UserRepository に「依存」している │
│ │
└─────────────────────────────────────────────────────────────┘
密結合の問題
依存先を「自分で決める」と問題が起きる。
┌─────────────────────────────────────────────────────────────┐
│ 密結合の問題 │
├─────────────────────────────────────────────────────────────┤
│ │
│ UserService の中で: │
│ 「私は PostgresRepository を使う!」と決めてしまう │
│ │
│ ┌────────────────┐ │
│ │ UserService │ │
│ │ │ ──────────→ PostgresRepository │
│ │ 「俺はこれを │ 具体的な │
│ │ 使う!」 │ 実装に依存 │
│ └────────────────┘ │
│ │
│ 問題: │
│ ├── テスト時に本物のDBが必要(モックに差し替えられない) │
│ ├── MySQL に変えたいときにコード修正が必要 │
│ └── UserService が「何を使うか」まで責任を持ってしまう │
│ │
└─────────────────────────────────────────────────────────────┘
疎結合を目指す
┌─────────────────────────────────────────────────────────────┐
│ 疎結合の状態 │
├─────────────────────────────────────────────────────────────┤
│ │
│ UserService: │
│ 「Repository が欲しい。どの実装かは知らない(知らなくていい)」│
│ │
│ 《インターフェース》 │
│ UserService ────→ Repository │
│ △ │
│ │ │
│ ┌────────────┼────────────┐ │
│ │ │ │ │
│ Postgres MySQL Mock │
│ Impl Impl Impl │
│ │
│ UserService は「インターフェース」にだけ依存 │
│ どの実装を使うかは知らない │
│ │
└─────────────────────────────────────────────────────────────┘
でも、動かすには実装が必要
インターフェースに依存すれば疎結合になる。しかし...
┌─────────────────────────────────────────────────────────────┐
│ インターフェースだけでは動かない │
├─────────────────────────────────────────────────────────────┤
│ │
│ UserService は Repository インターフェースに依存 │
│ │
│ でも、インターフェースは「契約」であって「実体」ではない │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Repository repository = ??? │ │
│ │ │ │
│ │ インターフェースはインスタンス化できない │ │
│ │ 実際に動かすには「具体的な実装」が必要 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 疑問: じゃあ誰が実装を用意するの? │
│ │
└─────────────────────────────────────────────────────────────┘
誰が実装を渡すか?
┌─────────────────────────────────────────────────────────────┐
│ 誰が実装を渡すか? │
├─────────────────────────────────────────────────────────────┤
│ │
│ 選択肢1: 自分で作る │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Repository repo = new PostgresRepository() │ │
│ │ │ │
│ │ → これだと密結合に戻ってしまう... │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 選択肢2: 外部から渡してもらう ★これがDI │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ UserService(Repository repo) { │ │
│ │ this.repo = repo // 渡されたものを使う │ │
│ │ } │ │
│ │ │ │
│ │ → 自分では作らない。もらうだけ。 │ │
│ │ → 何を渡すかは「外部」が決める │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 「実装を外部から渡してもらう」= 依存性注入(DI) │
│ │
└─────────────────────────────────────────────────────────────┘
依存性注入(DI)
DI = Dependency Injection
依存するオブジェクトを外部から渡してもらう設計パターン。
ここまでの流れを整理すると:
┌─────────────────────────────────────────────────────────────┐
│ ここまでの流れ │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 密結合は問題がある │
│ └→ 具体的な実装に依存すると変更・テストが困難 │
│ │
│ 2. インターフェースに依存すれば疎結合になる │
│ └→ でもインターフェースだけでは動かない │
│ │
│ 3. 動かすには具体的な実装が必要 │
│ └→ 自分で作ると密結合に戻る │
│ │
│ 4. 解決策: 実装は外部から渡してもらう │
│ └→ これが DI(依存性注入) │
│ │
└─────────────────────────────────────────────────────────────┘
DIのイメージ
┌─────────────────────────────────────────────────────────────┐
│ DIを日常で例えると... │
├─────────────────────────────────────────────────────────────┤
│ │
│ 【従来: 自分で調達】 │
│ │
│ 料理人が「俺は〇〇の肉しか使わない」と決めている │
│ │
│ → 肉の仕入先を変えたい場合、料理人を説得する必要がある │
│ → 料理人が仕入先に依存している(密結合) │
│ │
├─────────────────────────────────────────────────────────────┤
│ │
│ 【DI: 外部から渡す】 │
│ │
│ 料理人「肉をください。どこの肉かは問いません」 │
│ │
│ → 仕入先を自由に変更できる │
│ → テスト時は偽物の肉(モック)を渡せる │
│ → 料理人は「料理する」ことだけに集中できる │
│ │
└─────────────────────────────────────────────────────────────┘
DIの基本パターン
依存オブジェクトを渡す方法は基本的に2つ。
┌────────────────────────────────────── ───────────────────────┐
│ DIの注入方法 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. コンストラクタで渡す ★推奨 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ インスタンス生成時に渡す │ │
│ │ │ │
│ │ ✓ 依存が明確(何が必要か一目瞭然) │ │
│ │ ✓ 後から変更されない(不変性を保証) │ │
│ │ ✓ 必須の依存を強制できる │ │
│ │ │ │
│ │ → 必須の依存にはこれを使う │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 2. セッター(メソッド)で渡す │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 生成後にメソッドで渡す │ │
│ │ │ │
│ │ ○ オプションの依存に適している │ │
│ │ △ 設定忘れのリスクがある │ │
│ │ │ │
│ │ → オプションの依存にのみ使う │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
DIがもたらす柔軟性
┌─────────────────────────────────────────────────────────────┐
│ DIがもたらす柔軟性 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 同じサービスでも、何を注入するかで動作が変わる │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ UserService │ │
│ │ 「Repositoryをください」 │ │
│ └─────────────────────────── ───────────────────────────┘ │
│ │ │
│ 何を注入するかは「外部」が決める │
│ │ │
│ ┌───────────────────┼───────────────────┐ │
│ ↓ ↓ ↓ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ 本番用 │ │ 開発用 │ │テスト用 │ │
│ │ 実装 │ │ 実装 │ │ モック │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ │
│ UserServiceのコードは1行も変えずに │
│ 異なる環境で動作させることができる │
│ │
└─────────────────────────────────────────────────────────────┘
DIの本質
┌────────────────────────────────── ───────────────────────────┐
│ DIの本質 │
├─────────────────────────────────────────────────────────────┤
│ │
│ DIとは「どの実装を使うか」の決定を外部に委ねること │
│ │
│ ・「Repositoryが必要」← 自分で分かっている │
│ ・「どのRepositoryか」← 外部が決める(自分は知らない) │
│ │
│ これにより: │
│ ├── 本番では本番用の実装を使う │
│ ├── テストではモックを使う │
│ ├── 開発ではシンプルな実装を使う │
│ └── 将来の変更にも対応できる │
│ │
│ 重要: │
│ DIはフレームワークがなくても使える純粋な設計パターン │
│ 「依存を外から渡す」だけで実現できる │
│ │
└─────────────────────────────────────────────────────────────┘
DIコンテナ
DIコンテナとは
DIコンテナ(DI Container / IoC Container)は、オブジェクトの生成と依存関係の解決を自動で行う仕組み。
┌─────────────────────────────────────────────────────────────┐
│ DIコンテナ = 組み立て工場 │
├─────────────────────────────────────────────────────────────┤
│ │
│ あなたが「Controllerが欲しい」と言うと... │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ DIコンテナ │ │
│ │ │ │
│ │ 「Controller には Service が必要だな」 │ │
│ │ 「Service には Repository が必要だな」 │ │
│ │ 「Repository には DataSource が必要だな」 │ │
│ │ │ │
│ │ → 必要なものを全部揃えて組み立てて渡す │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ あなたは完成品を受け取るだけ │
│ 部品の調達や組み立ての順序は考えなくていい │
│ │
└─────────────────────────────────────────────────────────────┘
手動DI vs DIコンテナ
┌─────────────────────────────────────────────────────────────┐
│ 手動DI vs DIコンテナ │
├─────────────────────────────────────────────────────────────┤
│ │
│ 【手動DI = 自分で料理を作る】 │
│ │
│ ・材料を自分で買いに行く │
│ ・調理の順番を自分で考える │
│ ・全ての工程を自分で管理 │