以前に「【C# WPF】Dependency Injectionを実装してみる」でDependency Injectionを実装しました。
今回は、このプログラムに改良を加え「IoCコンテナ」という仕組みを使うようにしたいと思います。
「IoC コンテナ」について
以前に作成したプログラムでは、Domainのクラスを生成する都度、Infrastructureのクラスを生成してDomainに渡していました。
このままでは利用箇所が複数になるケースでは、同じようなコードが散在してしまいます。
特にインフラ関係は、差し替えたくなるニーズが多い。
テスト時に本番用ファイルからテスト用ファイルに差し替えたいとか、通信相手側のシステムが準備できていないのでダミーに差し替えたいとか、、、
なので、1箇所変更れば差し替えができるようにしておきたいものです。
その対策として、DomainのクラスとInfrastructureのクラスの紐つけを登録しておき、Domainのクラスを生成するだけで自動的に登録済みのInfrastructureのクラスも生成される仕組みが用意されています。
それが、IoCコンテナです。
英語でいうと “Inversion of Control Container”。直訳すると「制御を反転する仕組みを入れておく容器」かな?
では、実装してみましょう。
「IoC コンテナ」のインストール
IoCコンテナは、いくつかあるようですが、今回はMicrosoftが用意しているコンテナを使います。
まず、NuGetで「Microsoft.Extensions.DependencyInjection」をインストールします。
「ツール」メニュー → 「NuGetパッケージ マネージャー」→「ソリューションのNuGetパッケージの管理」と選んで進みます。
「NuGet – ソリューション」というタブが開くので「参照」を選び、検索窓に「Microsoft.Extensions.DependencyInjection」と入力して検索します。
検索結果が表示されるので「Microsoft.Extensions.DependencyInjection」を選択します。
右側にプロジェクトの一覧が表示されるので「Infrastructure」と「WpfApp1」の両方をチェックし「インストール」をクリック。
「変更後のプレビュー」という確認画面が出た場合は「OK」をクリック。
「インストール済み」という欄にバージョン番号が表示されたらOKです。
チェックボックスにチェックは、インストール済みという意味ではないので、必ずバージョン番号で確認するようにしてください。
「ソリューション エクスプローラ」で「Infrastructure」と「WpfApp1」の依存関係を確認してください。
もし、次の画像のように、黄色っぽい三角のアイコンがついていた場合は、次のように対応します。
そのプロジェクトを選択して、右クリックメニューから「プロパティ」を選び、プロジェクトのプロパティ・タブが開くので「ターゲットOS」を「Windows」にします。
「IoC コンテナ」の利用
「IoC コンテナ」を使ってみましょう。
「ViewModel.cs」を次のように変更します。
まず「ServiceCollection」を生成します。
そして、用意されている登録用のメソッドで「ValueObject1」を登録します。
続いてインターフェイスとそれを実装したクラスをセットで登録します。
登録用のメソッドは、次のどちらかを使います。
・AddSingleton … コンテナ内で単一のインスタンスを作成する
・AddTransient … 依存解決を要求するたびに新しいインスタンスを作成する
続いて「BuildServiceProvider」というメソッドで「ServiceCollection」から「ServiceProvider」を作成すると、IoCコンテナの準備が完了します。
利用する場合は「ServiceProvider」の「GetService」メソッドでDomainのクラスを生成します。
「Domain」プロジェクトと「Infrastructure」プロジェクトは前回と同じです。変更はありません。
これで、IoCコンテナの実装が完了です。
コンテナの準備からプロバイダを生成するまでは、最初に一度だけ実施すればOK。
IoCコンテナを使うメリットは、インフラを登録する場所が一カ所だけという点です。
ドメインのクラスを生成する時にインフラのことを気にせずにすみます。
もし、IoCコンテナを使わなければ、ドメインのクラスの生成をする時に、インフラのクラスの生成を必ずセットで行う必要がでてきます。
冒頭でも述べましたが、インフラは、テスト時とリリース時で差し替えたいとか、テスト内容によって差し替えたいなど、差し替えるというニーズが頻繁にでる箇所。あちこちで記述を変更するのことになるのは、かなり面倒です。IoCコンテナ使えば、このような面倒がなくなります。
なお、IoCコンテナのアンチパターン(禁じ手)として、「ServiceProvider」そのものを Domainのクラスの引数にセットして渡し、Domainのクラスの中で「GetService」メソッドを使ってInfrastructureのクラスを生成するという方法があります。これではIoCコンテナを使うメリットがなくなるのでNGです。
Domainをプロジェクトとして分けて依存関係を設定しておけば、ビルド時にエラーになるので防げます。
まとめ
今回は、Microsoftが用意している IoCコンテナを利用しました。
第三者が用意しているIoCコンテナもありますが、とりあえずは充分だと思います。
なお「IoCコンテナ」と同じ目的で利用できる「サービス・ロケータ」という仕組みもありますが、こちらはあまりオススメしません。
サービス・ロケータを使うとコードが分かりづらくなったり、サービス・ロケータへの依存で単体テストがしづらくなったりするからです。
例えば、IoCコンテナではコンパイル時にエラーを出してくれるケースでも、サービスロケータは実行した時に初めてエラーになるケースが多いので、開発やメンテナンスの効率が悪いからです。
コメント