次のように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())));
ProductLocator
、PricingService
などは、具象クラスではなく、インターフェイスとしてコンストラクタパラメータで呼び出されると想定しています。 IoCコンテナーは、インスタンス化するIProductLocator
、IPricingService
などの実装をどのようにして知るのですか?
IoCコンテナは、両方の依存関係に同じConfigProvider
を使用するのに十分スマートですか(それが要件である場合)?
あなたがそれを行う方法を指示する設定をそれに渡すので。通常、これはアプリケーションの起動時に発生します。
最も単純なケースでは、次のような多くの行を作成できます。
iocConfig.Bind<IShippingService>().To<ShippingService>();
このような構成を定義するには、さまざまな方法があります。たとえば、規則(例:I
プレフィックス以外のインターフェースの名前に一致する具体的なクラス)、属性、構成ファイルなど、それぞれに長所と短所があります。
テスト時には、スタブを手動で作成するか、別の構成を使用できます。
ほとんどのコンテナは、属性を追加するなど、ターゲットサイトが依存関係の解決に影響を与える方法もサポートしています。個人的には今のところ必要ありません。
インスタンスの再利用はscopeによって決定されます。通常、何も再利用されない(一時的な)1つのスコープ、リクエストごとのスコープ、スレッドごとのスコープ、シングルトンスコープなどがあります。通常、スコープは構成の一部として指定します。
コンテナにはセットアップが必要です。インターフェースの実装を求めるときに、何をしたいかを伝える必要があります。
実装が1つしかない場合、コンテナはそれを自動的に解決できる可能性がありますが、それでも2つのケースがあります。
1。実装は、コンストラクターパラメーターのない具象クラスにのみ依存します
その場合、コンテナは依存関係を認識し、すべて構築して注入するため、通常は依存関係を検索します。
2。実装は、文字列、intなどのタイプを必要とするインターフェイスまたはクラスに依存します
この場合、パラメーター(スカラー型であっても、具体的な実装であっても)は、特定のケースに対してもう一度構成する必要があります。
求めているインターフェースの実装が複数ある場合は、何をすべきかを指示する必要があります。
インターフェースがあり、実行時に実装を選択したい場合、IoCコンテナーを介して実際にそれを解決することはできないと思います。通常、ファクトリーアプローチをお勧めします。
具体的なIoC Castle Windsor を使用してこれに答え、これを具体的な例にまとめます。コンテナが異なれば、機能や命名規則も少し異なります。質問の詳細の一部は、実装固有のものになります。
コンポーネントを1つずつ登録する基本的な方法は2つあります。 1つずつの登録では、登録するコンポーネントとインターフェースをリストします。タイプを指定して、実装するインターフェースに登録することもできます。例
container.Register(
Component.For<IMyService>().ImplementedBy<MyServiceImpl>()
);
container.Register(
Component.For<MyServiceImpl>()
);
慣例により、登録ではいくつかのルールを指定でき、それに一致するタイプを登録します。特定のインターフェースを実装するタイプ、またはアセンブリに存在するすべてのタイプを登録できます。
暗黙の質問に答えるために、インターフェースの複数の実装がある場合にどうなるかについて。 Castle Windsorは、最初に登録された方を返します。これは少し不透明かもしれませんが、少なくとも一貫しています。
タイプを登録すると、ライフスタイルが与えられ、コンポーネントをいつ使用するかが記述されます。毎回新しいもの(一時的)からシングルトンまでの範囲のオプションがあります。したがって、ConfigProvider
が一時的なものとして登録されている場合は2つの異なるインスタンスになり、シングルトンの場合は1つのインスタンスになります。他にもいくつかのライフスタイルが利用可能であり、最も一般的なものはリクエストごとです。
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登録済みインスタンスを返します。上記の例を使用すると、ConsoleLogger
とFileLogger
の両方を取得するためにこれを実行するだけで済みます。
_public MyWorker(IEnumerable<ILogger> loggers) { ... }
_