制御の反転
フレームワークがアプリケーションコードを呼び出す設計原則で、依存性注入の基盤となる概念
制御の反転とは
制御の反転 (Inversion of Control, IoC) は、プログラムの制御フローをアプリケーションコードではなくフレームワークやコンテナが管理する設計原則である。「ハリウッドの原則 (Don't call us, we'll call you)」とも呼ばれる。
通常のライブラリ利用では、アプリケーションがライブラリを呼び出す。IoC では逆に、フレームワークがアプリケーションのコードを呼び出す。この「制御の方向の反転」が名前の由来だ。
// 通常: アプリケーション → ライブラリ (アプリが制御)
const server = new HttpServer();
server.listen(3000);
const request = server.waitForRequest(); // アプリが制御フローを管理
handleRequest(request);
// IoC: フレームワーク → アプリケーション (フレームワークが制御)
app.get('/users', (req, res) => {
// Express がリクエストを受け取り、適切なハンドラを呼び出す
// アプリは「何をするか」だけを定義し、「いつ呼ばれるか」はフレームワークに委ねる
});
IoC の実現手段
IoC は広い概念であり、複数の手段で実現できる。依存性注入 (DI) はその 1 つにすぎない。
| 手段 | 仕組み | 例 |
|---|---|---|
| 依存性注入 (DI) | 依存オブジェクトを外部から注入 | コンストラクタインジェクション |
| イベントリスナー | イベント発生時にフレームワークがリスナーを呼び出す | DOM イベント、EventEmitter |
| テンプレートメソッド | 基底クラスがアルゴリズムの骨格を定義し、サブクラスが詳細を実装 | React のライフサイクルメソッド |
| コールバック | 処理完了時にフレームワークがコールバックを呼び出す | Node.js の非同期 API |
| 設定ファイル | フレームワークが設定を読み取り、適切なコンポーネントを組み立てる | Spring の XML 設定 |
Lambda ハンドラーは IoC の典型例
AWS Lambda のハンドラー関数は IoC そのものだ。開発者は「何をするか」(ハンドラーの実装) だけを定義し、「いつ呼ばれるか」(API Gateway のリクエスト、SQS のメッセージ、EventBridge のイベント) は Lambda ランタイムが制御する。
DI による IoC の実践
依存性注入は IoC の中で最も実務的に重要な手段だ。テスト容易性とモジュールの疎結合を実現する。
IoC コンテナ
Java の Spring や .NET の ASP.NET Core には IoC コンテナ (DI コンテナ) が組み込まれている。コンテナが依存関係のグラフを解析し、必要なオブジェクトを自動的に生成・注入する。
TypeScript/Node.js では、tsyringe や InversifyJS が IoC コンテナを提供するが、Lambda のような短命なプロセスでは、手動の DI (コンストラクタインジェクション) で十分なケースが多い。コンテナの導入コストに見合うかを判断する。
よくある誤解
「IoC = DI」
DI は IoC の一手段であり、IoC そのものではない。Express のルーティング、React のコンポーネントライフサイクル、Lambda のイベントハンドラーもすべて IoC だ。
「IoC を使えば自動的にテストしやすくなる」
IoC (特に DI) はテスト容易性の前提条件だが、十分条件ではない。依存を注入可能にしても、インターフェースが巨大すぎたり、副作用が多すぎたりすれば、テストは依然として困難だ。
さらに掘り下げるなら関連書籍が参考になる。