章: 第1章: オブジェクト指向の深化
依存関係が増えると、手動DIは破綻する
DIを使い始めると、こんな問題が起きます。
// クラスが増えるほどこの初期化コードが膨れ上がる
$pdo = new PDO('mysql:...');
$repo = new PdoOrderRepository($pdo);
$mailer = new SmtpMailer('smtp.example.com');
$service = new OrderService($repo, $mailer);
依存関係が5個・10個と増えるにつれ、手動での生成コードは管理不能になります。DIコンテナはオブジェクトの生成と依存解決を自動化する仕組みです。実務では標準的に使われています。
DIコンテナの仕組み
DIコンテナは「インターフェイス → 実装クラス」の対応表を持ち、必要なときに自動生成・注入を行います。
<?php
// PHP-DIを使った例
use DI\ContainerBuilder;
$builder = new ContainerBuilder();
$builder->addDefinitions([
// LoggerInterfaceを要求されたらFileLoggerを使う
LoggerInterface::class => DI\create(FileLogger::class),
// OrderRepositoryを要求されたらPdoOrderRepositoryを使う
OrderRepository::class => DI\autowire(PdoOrderRepository::class),
]);
$container = $builder->build();
// コンテナが依存を自動解決してくれる
$service = $container->get(OrderService::class);
チェックポイント:
$container->get()を直接コード中で呼ぶのはサービスロケーターアンチパターンです。エントリーポイント(index.php等)でのみ使い、アプリケーションコードでは使わないようにしましょう。
手動DI vs DIコンテナ
| 観点 | 手動DI | DIコンテナ |
| 初期化コードの量 | 依存が増えるほど膨大になる | 定義ファイルに一元化 |
| 実装の切り替え | 初期化箇所を全て変更 | 定義ファイルを1行変更 |
| テスト時の差し替え | モックを手動で全て渡す | コンテナの定義をテスト用に上書き |
| 学習コスト | 低い(PHPだけで書ける) | コンテナライブラリの学習が必要 |
チェックポイント: 小さなプロジェクトでは手動DIで十分です。クラスが20〜30を超えてきたらコンテナの導入を検討しましょう。
よく使われるPHP DIコンテナ
- PHP-DI: 設定が直感的でLaravel以外で最も使いやすい
- Symfony DI: Symfonyエコシステムの標準、設定が詳細
- Pimple: 軽量でシンプル、小規模プロジェクト向け
- Laravel IoC Container: Laravelに組み込み済み、
app()->make()で利用
まとめ & 次のステップ
- DIコンテナはオブジェクトの生成と依存解決を自動化する
- インターフェイスと実装の対応を定義ファイルに一元管理することで、切り替えが容易になる
- サービスロケーターパターン(コード中で直接
get()を呼ぶ)は避け、コンストラクタ注入と組み合わせる
次回はStrategyパターンを学びます。DIと組み合わせることで、「処理のアルゴリズムを実行時に切り替える」柔軟な設計が実現できます。