コンテナーをコンストラクターに入れるのがなぜそんなに悪いのですか? たとえば、依存関係が解決された状態でクラス (B) を使用する必要があるため、別のクラス (C) のコンストラクターでクラス B を解決する必要があります (クラス C を B のように希望どおりに使用し始めますが、依存関係が解決されました)。
1 に答える
コンテナーをコンストラクターに入れるのがなぜそんなに悪いのですか?
コンテナーをコンストラクター引数として渡すつもりだと思います。これは実際には Service Locator パターンのバリエーションであり、このコンテキストではアンチ パターンと見なされます。これを実行したくない理由はいくつかあります。
まず、クラスのユーザーは、依存関係を解決するためにクラスがコンテナーを必要とすることしか知りません。この量の情報は、クラスが何に依存するのかまだわからないため、情報がまったくないことと同じです。クラスの単体テストを作成しますか? クラスの内部を調べて、解決されている型を確認し、それらをモックして、すべてのテストのコンテナーを初期化する必要があります。これは、一部のコードを変更すると、コンパイルは可能になりますが、一部のテストが壊れる可能性があることも意味します。これは、たとえば、新しいコードがコンテナーにまだ登録されていないクラスに依存している場合です。
Service Locator を使用する場合によくある二次的な影響は、実行時に依存関係を要求しているときに例外が発生しないという確信が持てないことです。すべてのクラスが正しく登録されていますか? 一部のコンテナーでは、すべてのインターフェースが登録されているかどうかを確認する機能が提供されていますが、正しいタイプに登録されているわけではありません。たとえば、型が 2 つの異なる実装で 2 回登録され、コードの一部がコンテナーを呼び出すことができるかどうかに気付くのが難しくなる可能性があります。
これに対するより良い解決策は、Composition Root パターンです。このブログ投稿では、Service Locator が適切でない理由についても説明しています。
新しい開発に照らして編集:
どうやら、デフォルトのコンストラクタを持つクラスに依存するサードパーティのライブラリを使用しているようです。クラスのインスタンス化に影響を与える方法がなく、このフレームワークに仕事をさせなければならないと仮定しましょう。これは大きな仮定である可能性があることに注意してください。最初にサードパーティのライブラリで可能性を調査してください。一見すると、ASP.NET WebForms や WCF などのフレームワークではチャンスがあまりありませんが、これらのケースの痛みを軽減する方法があります。
コンストラクターでコンテナーを作成し、それぞれの依存関係をコンテナーに追加してオブジェクトを解決することを意味しました。これは、依存オブジェクトのインスタンスを作成し、それを使用して依存オブジェクトを作成するだけで実行できます。
何かが足りないかもしれませんが、コンストラクターに依存関係を登録する必要があるのはなぜですか? コンストラクターで解決して、別の場所に登録することはできませんか? それは依然として Service Locator ですが、少なくとも間違ったことを正しく行っていることになります。
なぜコンストラクタでそうするのが悪い考えで、他の場所でそうするのが良いのでしょうか?
1 か所以外の場所でこれを行うのは悪い考えです。なぜコンテナ登録をあちこちに広めるのですか? 実行時に使用するインターフェイスの実装を決定する必要がある場合は、Factory などを使用してください。
それで、なぜそれは悪いのですか?
- クライアント クラスは実装とインターフェイスの両方に依存するため、コンストラクターで具象クラスを新しくするよりも優れているわけではありません。
- クライアント クラスもコンテナーに依存するようになり、Service Locator の問題が発生し (上記を参照)、このアプローチは具象クラスを新しくするよりも悪くなります。
@Stevenが言ったように、依存性注入のすべての利点を失います。本当の根底にある質問は、なぜこの場所で絶対にDIをしたいのですか? このアプローチのどのような利点を利用したいですか? 答えに基づいて、いくつかの解決策が考えられます。私の頭の上から2つの例:
解決策 1 : サード パーティ ライブラリによってインスタンス化されているクラスの DI を失います。
解決策 2 : Bastard インジェクションと Service Locator を組み合わせて使用します。この場合、2 つの過ちが正しい可能性があります。
public class MyClass
{
public MyClass()
: this(Container.Resolve<IDependency>())
{
}
public MyClass(IDependency dep)
{
}
}
この場合、Service Locator によって解決されるため、コンストラクターで非ローカル依存関係を使用していないため、実装に依存関係がありません。