メインコンテンツまでスキップ

IoC と DI - 制御の反転と依存性注入

このドキュメントの目的

IoC(制御の反転)DI(依存性注入) の概念を深く理解し、なぜこれらがモダンなフレームワークの基盤となっているかを学びます。


目次

  1. IoC(制御の反転)とは
  2. 依存関係と密結合の問題
  3. 依存性注入(DI)
  4. DIコンテナ
  5. アプリケーションの初期化
  6. まとめ

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 = 自分で料理を作る】 │
│ │
│ ・材料を自分で買いに行く │
│ ・調理の順番を自分で考える │
│ ・全ての工程を自分で管理 │
│ │
│ メリット: 完全にコントロールできる、何が起きているか明確 │
│ デメリット: 依存が増えると大変、順番を間違えやすい │
│ │
├─────────────────────────────────────────────────────────────┤
│ │
│ 【DIコンテナ = レストランで注文する】 │
│ │
│ ・メニュー(設定)を渡すだけ │
│ ・材料の調達はシェフ(コンテナ)任せ │
│ ・完成品が出てくる │
│ │
│ メリット: 楽、依存関係を自動解決 │
│ デメリット: 内部が見えにくい(魔法に見える) │
│ │
└─────────────────────────────────────────────────────────────┘

DIコンテナの責務

┌─────────────────────────────────────────────────────────────┐
│ DIコンテナの3つの責務 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 登録(Registration) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 「この型が欲しいときは、この実装を使う」 │ │
│ │ │ │
│ │ UserRepository → PostgresUserRepository │ │
│ │ Logger → FileLogger │ │
│ │ │ │
│ │ インターフェースと実装のマッピングを管理 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 2. 解決(Resolution) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 「この型のインスタンスをください」 │ │
│ │ │ │
│ │ 依存関係を辿り、必要なものを全て生成 │ │
│ │ 正しい順序で組み立て │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 3. ライフサイクル管理 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ インスタンスをいつ作り、いつ捨てるか │ │
│ │ │ │
│ │ ・Singleton: アプリ全体で1つだけ │ │
│ │ ・Prototype: 要求されるたびに新規作成 │ │
│ │ ・Scoped: 特定の範囲内で共有 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘

DIコンテナを使うべきか?

┌─────────────────────────────────────────────────────────────┐
│ DIコンテナを使うべきか? │
├─────────────────────────────────────────────────────────────┤
│ │
│ 使うと良い場合: │
│ ├── 依存関係が多い・深い │
│ ├── 複数の実装を切り替えたい │
│ ├── フレームワークが提供している │
│ └── チームで統一したやり方が欲しい │
│ │
│ 使わなくても良い場合: │
│ ├── 小規模なアプリケーション │
│ ├── 依存関係がシンプル │
│ ├── 手動DIで十分管理できる │
│ └── 「魔法」を減らしたい │
│ │
│ 重要: │
│ DIコンテナはDIを「便利にする道具」であり、 │
│ DI自体はコンテナなしでも実践できる │
│ │
└─────────────────────────────────────────────────────────────┘

アプリケーションの初期化

どこで組み立てるか?

DIでは「外部から渡す」と言ったが、その「外部」とは具体的にどこか?

┌─────────────────────────────────────────────────────────────┐
│ どこで依存関係を組み立てるか │
├─────────────────────────────────────────────────────────────┤
│ │
│ アプリケーションには必ず「起点」がある │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ main() とか Application.start() とか │ │
│ │ │ │
│ │ → ここが「組み立て」を行う場所 │ │
│ │ → 「コンポジションルート」と呼ばれる │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ コンポジションルート = 依存関係を組み立てる唯一の場所 │
│ │
└─────────────────────────────────────────────────────────────┘

アプリケーション起動の流れ

┌─────────────────────────────────────────────────────────────┐
│ アプリケーション起動の流れ │
├─────────────────────────────────────────────────────────────┤
│ │
│ 【手動DIの場合】 │
│ │
│ main() { │
│ // 1. 依存関係を組み立てる(コンポジションルート) │
│ repository = new PostgresRepository() │
│ service = new UserService(repository) │
│ controller = new UserController(service) │
│ │
│ // 2. アプリケーションを実行 │
│ app.run(controller) │
│ } │
│ │
├─────────────────────────────────────────────────────────────┤
│ │
│ 【DIコンテナを使う場合】 │
│ │
│ main() { │
│ // 1. コンテナを初期化 │
│ container = new DIContainer() │
│ │
│ // 2. 依存関係を登録 │
│ container.register(Repository, PostgresRepository) │
│ container.register(UserService) │
│ container.register(UserController) │
│ │
│ // 3. ルートオブジェクトを取得(自動で組み立て) │
│ controller = container.resolve(UserController) │
│ │
│ // 4. アプリケーションを実行 │
│ app.run(controller) │
│ } │
│ │
└─────────────────────────────────────────────────────────────┘

フレームワークが隠す部分

┌─────────────────────────────────────────────────────────────┐
│ フレームワークが隠す部分 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 多くのフレームワーク(Spring, NestJS 等)では │
│ このコンポジションルートを「フレームワーク」が担当する │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ あなたが書くこと: │ │
│ │ ・クラスに「これは Service です」とマークする │ │
│ │ ・コンストラクタで「何が欲しいか」を宣言する │ │
│ │ │ │
│ │ フレームワークがやること: │ │
│ │ ・マークされたクラスを見つける │ │
│ │ ・依存関係を解析する │ │
│ │ ・正しい順序で組み立てる │ │
│ │ ・アプリケーションを起動する │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ これも IoC: │
│ 「組み立て」の制御がフレームワークに移っている │
│ │
└─────────────────────────────────────────────────────────────┘

コンポジションルートの原則

┌─────────────────────────────────────────────────────────────┐
│ コンポジションルートの原則 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ✓ 依存関係の組み立ては1箇所で行う │
│ └→ アプリの起点(エントリーポイント) │
│ │
│ ✓ ビジネスロジックは依存の組み立てを知らない │
│ └→ 渡されたものを使うだけ │
│ │
│ ✓ 「何を使うか」の決定は起動時に行う │
│ └→ 実行中に依存が変わることはない(通常) │
│ │
│ これにより: │
│ ├── 設定変更だけで実装を切り替えられる │
│ ├── テスト時は別の組み立てができる │
│ └── 各クラスは自分の責務に集中できる │
│ │
└─────────────────────────────────────────────────────────────┘

まとめ

IoC と DI の関係

┌─────────────────────────────────────────────────────────────┐
│ IoC と DI の関係 │
├─────────────────────────────────────────────────────────────┤
│ │
│ IoC(制御の反転) │
│ └── 広い概念: 制御の主体を反転させる考え方 │
│ └── 「あなたが呼ぶ」から「呼ばれる」へ │
│ │
│ DI(依存性注入) │
│ └── IoCを実現する具体的な手法の一つ │
│ └── 「何を使うか」の決定を外部に委ねる │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ IoC │ │
│ │ ┌───────────────────────────────────────────────┐ │ │
│ │ │ DI │ │ │
│ │ └───────────────────────────────────────────────┘ │ │
│ │ コールバック、テンプレートメソッド、etc. │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘

覚えておくべきこと

概念説明
IoC制御の主体を反転させる考え方
DI依存するオブジェクトを外部から渡す設計パターン
密結合具体的な実装に直接依存している状態(避けるべき)
疎結合抽象(インターフェース)に依存する状態(目指すべき)
DIコンテナ依存関係の解決を自動で行う仕組み

ベストプラクティス

┌─────────────────────────────────────────────────────────────┐
│ ベストプラクティス │
├─────────────────────────────────────────────────────────────┤
│ │
│ ✓ コンストラクタで依存を受け取る │
│ ✓ 抽象(インターフェース)に依存する │
│ ✓ 依存は不変(final/readonly)にする │
│ ✓ 循環依存を避ける(A→B→A は設計を見直す) │
│ │
│ ✗ 具体的なクラスに依存しない │
│ ✗ 自分で依存オブジェクトを生成しない │
│ │
│ これらは言語やフレームワークに関係なく適用できる原則 │
│ │
└─────────────────────────────────────────────────────────────┘

次のステップ


練習問題

Q1: IoC とは何か?

答えを見る

Inversion of Control(制御の反転)

従来はアプリケーションがライブラリを「呼び出す」側だったが、 フレームワークではフレームワークがアプリケーションを「呼び出す」側になる。

制御の主従関係が反転していることから、この名前がついている。

「Don't call us, we'll call you.」(ハリウッドの原則)とも呼ばれる。

Q2: DI と DIコンテナの違いは?

答えを見る

DI(依存性注入): 設計パターン

  • 依存するオブジェクトを外部から渡してもらう考え方
  • フレームワークなしでも実践できる

DIコンテナ: ツール/仕組み

  • DIを自動化・便利にするための道具
  • 依存関係の解決、ライフサイクル管理などを行う

DIコンテナがなくてもDIは実践できる(手動DI)。

Q3: なぜ密結合を避けるべきか?

答えを見る

密結合の問題:

  1. テストが困難: モックに差し替えられない
  2. 変更が困難: 実装を変えるとコード修正が必要
  3. 責務が曖昧: 「何を使うか」まで決めてしまう

疎結合のメリット:

  1. テスト容易: モックを自由に注入できる
  2. 変更容易: 注入する実装を変えるだけ
  3. 責務明確: 「使う」ことだけに集中できる