🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
DI (Dependence Injection)

DI (Dependence Injection)は、「依存性の注入」という概念を指します。オブジェクト間で依存性のあるコードを外部の設定ファイルから注入するソフトウェアパターン設計思想です。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

解決済

1回答

1529閲覧

依存性を注入されるクラスを利用する場合はサービスロケータ的にする必要がある?

ry188472

総合スコア74

DI (Dependence Injection)

DI (Dependence Injection)は、「依存性の注入」という概念を指します。オブジェクト間で依存性のあるコードを外部の設定ファイルから注入するソフトウェアパターン設計思想です。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

0グッド

1クリップ

投稿2021/01/26 07:00

編集2021/01/27 01:21

現在、密結合だらけでバグ修正等が大変なことになっているプログラムの全面的な再開発をしており、DIコンテナを活用して解決をしていこうとしています。
調べていくうちにDIコンテナの使い方で混乱してきたので、どなたかご教授願います。

コンテナに依存?

以下のようなシンプルなクラスを考えます。LogicAクラスはIServiceに依存していて、その実体はコンストラクタインジェクションするとします。

c#

1// 注入したいインターフェース 2public interface IService { void Call(); } 3// DIコンテナでインターフェースにマップされる具象 4public class ServiceA : IService { void Call() { /* 処理 */ } } 5// インターフェースを注入されるクラス 6public class LogicA { 7 private IService _service; 8 public LogicA(IService service) { _service = service; } 9 public void Call() { _service.Call(); } 10}

このLogicAクラスを利用する場合、LogicAのインスタンス生成時にIServiceの実体がないといけません。IServiceの実体の作り方はコンテナが知っています。つまり、

c#

1public class LogicAUser { 2 public void CallLogicA() { 3 // マップ済のコンテナから生成 4 // コンテナインスタンスは、グローバル変数のコンテナまたはコンストラクタで受領 5 var service = container.Resolve<IService>(); 6 var logic = new LogicA(service); 7 logic.Call(); 8 } 9}

とする必要があると思うのですが、結局LogicAUserクラスはコンテナに依存した作りになってしまいます。

質問事項

  1. これって「DIコンテナをサービスロケーター的に使う」というアンチパターンになってしまっているように見えるのですが、使い方は合っていますか?
  2. 個人的な理解としては、LogicAUserを含むアセンブリの参照をするアセンブリがコントロールできるなら問題ない(そもそもDIコンテナを使っている時点でそれに依存しているため)、そうでないならコンテナに依存してしまうんで問題がある、という認識ですが、合っていますか?
  3. 問題ない場合、コンテナへの参照はどのように利用クラスに引き渡すべきですか?極論をいえばDIコンテナで生成する処理がスレッドセーフであればどうでもいいですか?
  4. 生成部分をコンテナに依存しないようにするにはどのようにすべきですか?ILogicAFactoryインターフェースとLogicAFactoryクラスを作ってLogicAUserクラスにILogicAFactoryをコンストラクタインジェクションする方法が思いつきましたが、冗長になりすぎる気がします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

gentaro

2021/01/27 01:41

「生成部分をコンテナに依存しないようにする」って、DI使う意味を全否定してると思うんだけども、何をしたいんでしょう。
guest

回答1

0

ベストアンサー

LogicAのインスタンス化はDIコンテナに解決させます。

ただその前にLogicAクラスのコンストラクタで必要となるIServiceをコンストラクタインジェクションするために、IServiceインターフェイスに対してどのクラスで解決するのかをDIコンテナに登録する手順を踏む必要があります。

void RegisterTypes(Container container) { container.Register<IService, Service>(); }

(疑似コードのためお使いのDIコンテナでのメソッドで置き換えて考えてください)

そして、型を解決するときはIServiceを解決して手動でnew LogicA(service)するのではなく、LogicAをDIコンテナで直接解決させます。DIコンテナが内部的に登録された型のマップを使ってLogicAへのコンストラクタインジェクションを実行し、LogicAがコンストラクタ引数で必要とする型が解決された状態でLogicAのインスタンスが生成されます。

var logicA = container.Resolve<LogicA>();

さらに、ここで例えばServiceがさらに別のクラスにコンストラクタ引数から依存していた場合でもDIコンテナに型を登録してあれば、あくまでcontainer.Resolve<LogicA>()とするだけで生成処理が完了します。

このようにDIコンテナは複雑なクラスの生成処理を「事前の型の登録」によって自動化することが出来ます。

投稿2021/01/27 10:24

編集2021/01/27 10:29
tor4kichi

総合スコア769

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ry188472

2021/01/28 02:26

var logicA = container.Resolve<LogicA>(); このコードを実行しているクラスはDIコンテナに依存していると思うのですが、それをどう解決すればよいですか?
tor4kichi

2021/01/28 03:44

アプリケーションをハンドルする部分のどこかでDIコンテナへの依存は発生させなければならないですが、それはアプリの実装方針に依って決定します。 GUIアプリであれば、ページやダイアログなど機能提供の画面的な区切りのタイミングをフレームワークで提供して、そこでだけDIコンテナによるクラス生成を行わせる、といった流れになるかと思います。 コンソールアプリであればMain内、初期化部分でのみContainerを使い解決します。 いずれにしろ、基本的にロジッククラスやビジネスロジックと呼ばれるようなレイヤーのクラスはDIコンテナに関わらせないことで個別にクラスをテストしやすくなります。あるいはDIコンテナの実装を差し替える場面でコード修正を少なく抑えられるなど効果があると思います。
ry188472

2021/01/28 04:09

Main関数などの初期化処理でのみコンテナを使用するのがいいのかな、とは思っていました。確かに、生成が必要なタイミング(画面遷移とか)の区切りをライブラリ等に区切って、そこだけはDIコンテナに依存する。実際のロジック等はコンストラクタインジェクションを使うようにする。 というのが良さそうですね。ある意味、DIコンテナのラッパーライブラリを作るイメージでしょうか。
tor4kichi

2021/01/29 07:51

アプリに実装する段ではDIコンテナのラッパーライブラリを作るようなイメージではあると思います。 DIコンテナの活用を前提としたフレームワーク(ライブラリ)を構築して、実際のページ部分、アプリケーションのコアとなる価値を生む部分の作成を効率化できるといいですよね。 ちなみにフレームワークとしてはPrismやReactiveUIなどが既にありますので、参考になるかもしれません。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問