MS_IoC - NetDevInfraWGinOSSConsortium/NetDevInfraWiki GitHub Wiki
-
制御の反転
-
IoC: Inversion of Control
-
IoC とは?
- 個別の目的のために書かれたコード部分が、
一般的で再利用可能なライブラリによるフロー制御を受ける形の設計。 - 伝統的な手続き型プログラミングと比べると制御の方向が反転しているため、
「制御の反転」と呼ばれる。
- 個別の目的のために書かれたコード部分が、
補足(「反転」しているのは呼び出しの向き): 一言でいうと
「あなたが呼ぶのではなく、あなたが呼ばれる」
(ハリウッド原則: Don't call us, we'll call you)である。【伝統的(ライブラリ)】 あなたのコード ──呼ぶ──> ライブラリ main() { var s = File.ReadAllText(...); Parse(s); Print(s); } = 制御の流れはあなたが握っている 【IoC(フレームワーク)】 フレームワーク ──呼ぶ──> あなたのコード [HttpGet] public IActionResult Index() { ... } = いつ呼ばれるかはフレームワークが決めるライブラリとフレームワークの違いはまさにここにあり、
「IoC があるかどうか」で分類できる。ASP.NET Core のコントローラー、イベント ハンドラー、
IHostedService、単体テストの[Fact]メソッド……
日常的に書いているコードの多くは、既に IoC の下にある。
-
あるタスクの実行を実装から分離する。
- モジュールを置き換える際の副作用を予防する。
-
あるモジュールを、目的とするタスクだけに集中させる。
- 仮定しながらのコーディングから解放し、
契約に依拠してコーディングさせる(契約プログラミング)。
- 仮定しながらのコーディングから解放し、
補足(「仮定しながらのコーディング」からの解放): 原文の
この表現は要点を突いている。// 仮定しながらのコーディング // 「たぶん設定ファイルはここにある」「たぶん接続は張られている」 var conn = new SqlConnection(File.ReadAllText(@"C:\app\conn.txt")); // 契約に依拠したコーディング // 「IDbConnection が渡ってくる」ということだけを前提にする public OrderRepository(IDbConnection conn) { ... }後者は、前提が引数の型として明示されているため、
- テストで差し替えられる、
- 前提が変わればコンパイル エラーになる、
- 読めば依存が分かる(明示的な依存関係の原則)、
という利点が生まれる。
デザインパターンの例
- ソフトウェアフレームワーク
- コールバック
- スケジューラ
- イベントループ
- 依存性の注入(DI)
オブジェクト指向プログラミングや、
その他のプログラミング・パラダイムにて応用される。
オブジェクト指向プログラミングにおける幾つかの基本的な技法
-
主要な技法
-
テンプレート・メソッド・パターン
簡単。派生クラスでメソッドをオーバーライドする。
-
-
その他の技法
-
Factory パターン
- サービス(オブジェクト)の取得を抽象化するパターン
- 内部に Template Method パターンを包含することが多い。
-
ストラテジー・パターン
- アルゴリズムを差し替えるためのパターン
- 継承に依る多態、委譲や関数ポインタに依るリフレクションのパターンがある。
-
サービスロケータ・パターン > 文脈化された参照
- インターフェイスと実装の対をコレクションに保存、必要なときに取り出して利用。
- Factory パターンに似ているが、必ずしも毎回インスタンスを生成するとは限らない。
-
移行メモ(誤字): 原文の「アルゴリズム差し替えるための」は
**「アルゴリズムを差し替えるための」**の脱字と判断し修正した。
補足(サービス ロケータはアンチパターン扱い): 原文が最後に挙げる
サービス ロケータは、現在では避けるべきとされることが多い。// サービス ロケータ:依存がコードの中に隠れる public void Do() { var repo = ServiceLocator.Get<IOrderRepository>(); // ← 外から見えない } // DI(constructor 注入):依存がシグネチャに現れる public OrderService(IOrderRepository repo) { ... } // ← 外から見える
サービス ロケータ DI(constructor 注入) 依存の可視性 隠れる シグネチャに現れる 未登録の検出 実行時(NullReference 等) 起動時(コンテナー検証) テスト ロケータ自体の差し替えが要る 引数を渡すだけ コンテナーへの依存 業務コードに侵入する 合成ルートだけ ASP.NET Core でも
IServiceProviderを直接注入して
GetService<T>()を呼ぶことは可能だが、
それはサービス ロケータであり、原則として避ける。
例外は、実行時にしか型が決まらない場合
(ファクトリ、プラグイン)に限られる。
その他のプログラミング・パラダイムにおける幾つかの基本的な技法
- ・・・
補足(原文が空欄のため補う): 関数型やリアクティブの文脈でも、
制御の反転は広く現れる。
パラダイム IoC にあたるもの 関数型 高階関数( Where(predicate)は「述語を呼んでもらう」)リアクティブ Rx(Push 型。送り手が呼ぶ) 非同期 async/await(継続を渡して呼んでもらう) イベント駆動 メッセージ キュー、Webhook、FaaS 特に FaaS(Azure Functions 等)は IoC の極端な形で、
「イベントが来たら関数を呼ぶ」以外の制御をアプリ側が持たない。
依存性反転原則とは関係しているが異なる。
補足: この一文は重要なので明示しておく。
- IoC … 制御の流れが反転している(誰が呼ぶか)
- DIP … 依存の向きが反転している(誰が誰を知っているか)
両者はよく同時に現れるが、独立した概念である。
整理は IoC、AOP → DI → 依存性反転原則 を参照。
- 制御の反転
https://ja.wikipedia.org/wiki/%E5%88%B6%E5%BE%A1%E3%81%AE%E5%8F%8D%E8%BB%A2 - 契約プログラミング
https://ja.wikipedia.org/wiki/%E5%A5%91%E7%B4%84%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0
Tags: 移行, プログラミング, .NET開発