web-dev-qa-db-ja.com

Dependency Injection / IOC Containerは、どの実装を使用するかをどのようにして知るのですか?

次のようにIoCコンテナを使用する場合:

var svc = IoC.Resolve<IShippingService>();

IoCコンテナーは、インスタンス化するIShippingServiceの実装をどのように選択しますか?

さらに、同等のコードを置き換えるために上記を呼び出す場合:

var svc = new ShippingService(new ProductLocator(), 
   new PricingService(), new InventoryService(), 
   new TrackingRepository(new ConfigProvider()), 
   new Logger(new EmailLogger(new ConfigProvider())));

ProductLocatorPricingServiceなどは、具象クラスではなく、インターフェイスとしてコンストラクタパラメータで呼び出されると想定しています。 IoCコンテナーは、インスタンス化するIProductLocatorIPricingServiceなどの実装をどのようにして知るのですか?

IoCコンテナは、両方の依存関係に同じConfigProviderを使用するのに十分スマートですか(それが要件である場合)?

5
Robert Harvey

あなたがそれを行う方法を指示する設定をそれに渡すので。通常、これはアプリケーションの起動時に発生します。

最も単純なケースでは、次のような多くの行を作成できます。

iocConfig.Bind<IShippingService>().To<ShippingService>();

このような構成を定義するには、さまざまな方法があります。たとえば、規則(例:Iプレフィックス以外のインターフェースの名前に一致する具体的なクラス)、属性、構成ファイルなど、それぞれに長所と短所があります。

テスト時には、スタブを手動で作成するか、別の構成を使用できます。


ほとんどのコンテナは、属性を追加するなど、ターゲットサイトが依存関係の解決に影響を与える方法もサポートしています。個人的には今のところ必要ありません。


インスタンスの再利用はscopeによって決定されます。通常、何も再利用されない(一時的な)1つのスコープ、リクエストごとのスコープ、スレッドごとのスコープ、シングルトンスコープなどがあります。通常、スコープは構成の一部として指定します。

4
CodesInChaos

コンテナにはセットアップが必要です。インターフェースの実装を求めるときに、何をしたいかを伝える必要があります。

実装が1つしかない場合、コンテナはそれを自動的に解決できる可能性がありますが、それでも2つのケースがあります。

1。実装は、コンストラクターパラメーターのない具象クラスにのみ依存します

その場合、コンテナは依存関係を認識し、すべて構築して注入するため、通常は依存関係を検索します。

2。実装は、文字列、intなどのタイプを必要とするインターフェイスまたはクラスに依存します

この場合、パラメーター(スカラー型であっても、具体的な実装であっても)は、特定のケースに対してもう一度構成する必要があります。

求めているインターフェースの実装が複数ある場合は、何をすべきかを指示する必要があります。


インターフェースがあり、実行時に実装を選択したい場合、IoCコンテナーを介して実際にそれを解決することはできないと思います。通常、ファクトリーアプローチをお勧めします。

3
Andy

具体的なIoC Castle Windsor を使用してこれに答え、これを具体的な例にまとめます。コンテナが異なれば、機能や命名規則も少し異なります。質問の詳細の一部は、実装固有のものになります。

コンポーネントを1つずつ登録する基本的な方法は2つあります。 1つずつの登録では、登録するコンポーネントとインターフェースをリストします。タイプを指定して、実装するインターフェースに登録することもできます。例

container.Register(
   Component.For<IMyService>().ImplementedBy<MyServiceImpl>()
);
container.Register(
    Component.For<MyServiceImpl>()
);

慣例により、登録ではいくつかのルールを指定でき、それに一致するタイプを登録します。特定のインターフェースを実装するタイプ、またはアセンブリに存在するすべてのタイプを登録できます。

暗黙の質問に答えるために、インターフェースの複数の実装がある場合にどうなるかについて。 Castle Windsorは、最初に登録された方を返します。これは少し不透明かもしれませんが、少なくとも一貫しています。

タイプを登録すると、ライフスタイルが与えられ、コンポーネントをいつ使用するかが記述されます。毎回新しいもの(一時的)からシングルトンまでの範囲のオプションがあります。したがって、ConfigProviderが一時的なものとして登録されている場合は2つの異なるインスタンスになり、シングルトンの場合は1つのインスタンスになります。他にもいくつかのライフスタイルが利用可能であり、最も一般的なものはリクエストごとです。

2
Sign

Autofac のメカニズムにも言及することで、このトピックにさらに色を付けていると思います。

@Signで言及されているCastleWindsorとは異なり、Autofacは登録されているlastのいずれかを返します。証拠として Autofac docs を引用します:

複数のコンポーネントが同じサービスを公開する場合、Autofacは最後に登録されたコンポーネントをそのサービスのデフォルトプロバイダーとして使用します。

_builder.Register<ConsoleLogger>().As<ILogger>();
builder.Register<FileLogger>().As<ILogger>();
_

このシナリオでは、最後に登録されたFileLoggerがILoggerのデフォルトになります。

上記のドキュメントでは、登録呼び出しの最後のPreserveExistingDefaults()を追加することで、その動作をオーバーライドできると述べています。

しかし、ここで他の回答で言及されていない別の興味深いユースケースはこれです:実際にall登録されたインスタンスが必要な場合はどうですか?

Autofacを使用すると、これは簡単です。登録とは関係ありません。それはあなたがそれを使う方法です。 _IEnumerable<>_の解決を要求するコンストラクターがある場合、それはall登録済みインスタンスを返します。上記の例を使用すると、ConsoleLoggerFileLoggerの両方を取得するためにこれを実行するだけで済みます。

_public MyWorker(IEnumerable<ILogger> loggers) { ... }
_
2
Michael Sorens