依存関係の逆転の原則(DIP)が適切に理解できていると思います。私の混乱は、依存関係の注入に関するものです。
私の理解は、インターフェイスが変更されないことを前提として、DIの全体のポイントは、アプリケーションの一部を分離し、ある部分を別の部分に影響を与えずに変更できるようにすることです。
例のために、私たちはこれを持っています
public class MyClass(IMyInterface interface)
{
public MyClass
{
interface.DoSomething();
}
}
public interface IMyInterface
{
void DoSomething();
}
これはどのように
var iocContainer = new UnityContainer();
iocContainer.Resolve<MyClass>();
これを行うよりも良い練習
//if multiple implementations are possible, could use a factory here.
IMyInterface interface = new InterfaceImplementation();
var myClass = new MyClass(interface);
私は非常に重要なポイントを欠いているかもしれませんが、私は何が得られるのか見落としています。 IOCコンテナーを使用すると、オブジェクトのライフサイクルを簡単に処理できることを認識しています。これは+1ですが、IOCの中心的な部分ではないと思います。
編集
次に、拡張された例を示します。
void Main()
{
IRepository repository = new AddRepository();
var maths = new PointlessMaths(repository,5);
Console.WriteLine(maths.Result);
}
public interface IRepository
{
int DoSomething(int id);
}
public class AddRepository : IRepository
{
public int DoSomething(int id)
{
return id + 1;
}
}
public class SubtractRepository : IRepository
{
public int DoSomething(int id)
{
return id - 1;
}
}
public class PointlessMaths
{
public int Result {get;set;}
public PointlessMaths(IRepository repository, int id)
{
Result = repository.DoSomething(id);
}
}
あなたの例では、あなたは何も得ていません。あなたの直感は正しいです。インターフェースを実装するクラスが1つしかない場合、それは常にそのままである可能性があるため、利益はありません。インターフェースを追加してコードを乱雑にする程度に制限はありません。
この例では、DIコンテナーの使用を「ベタープラクティス」とは呼びません。ただし、コードベースは大きくなり、複雑になる傾向があります。次のような依存関係グラフがあるとします。
A
/ \
/ \
B C
\ /
\ /
D
DIコンテナーなしでこのグラフを作成するコードは、次のようになります。
ID d = new D();
IB b = new B(d);
IC c = new C(d);
A a = new A(b, c);
Dの同じインスタンスがBとCに使用されていることに注意してください。しかし、それぞれが独自のインスタンスを必要とすると決めたらどうなるでしょうか。ここで、コードを複製してDの新しいインスタンスを作成する必要があり、結果は次のようになります。
ID d1 = new D();
IB b = new B(d1);
ID d2 = new D();
IC c = new C(d2);
A a = new A(b, c);
重要なアプリケーションは、これよりはるかに複雑な依存関係グラフを簡単に持つことができ、コードの複雑さはそれに応じてスケーリングされます。上記と、DIコンテナーを使用する同等のコードを比較してください。
var iocContainer = new UnityContainer();
iocContainer.Resolve<A>();
複雑さは依存関係の数に応じてしないことに注意してください。コンテナの構成を考慮した場合でも、unique依存関係の数に応じてのみスケーリングされます(たとえば、Dのエントリが1つだけ必要です100個のインスタンスを作成します)。さらに、一部の依存関係(具象クラスなど)は、まったく構成しなくても自動的に解決できます。
DIコンテナは、Dをシングルトンにするかどうか(たとえば)を構成する際に、他の多くの解決、構築、およびライフサイクルオプションに加えて、柔軟性を提供します。これらは、コンテナの設定構文を使用して、自分で実装するよりも読みやすい場合があります。
ディペンデンシーグラフが複雑になるほど、DIコンテナーから得られるメリットが増えます。
また、一般的なDIコンテナーは、特定のプラットフォーム(ASP.NET MVC、WCFなど)とよく統合されています。
インターフェイスの実装を渡すことをお勧めします。実際のメリットには、より複雑な例が付属しています。作成がランタイムの動作によって決定されるとどうなりますか?インスタンスではなくファクトリを渡すことで解決しますが、問題が発生する可能性があります。また、作成の時間枠が明確でなかった場合はどうなりますか?.
さらに、インスタンス化しているクラスも具象バージョンでない場合はどうなりますか?必要なのがIFoo
だけの場合、いくつかのファクトリが実装を提供しています。いくつかの具体的な実装は、いくつかの依存関係を注入する必要があり、他の実装は他の依存関係を必要とします(またはなし)。
IoCコンテナは巨大なハンマーです。 通常インターフェースの具体的な実装を渡すだけで、明確で機能的で柔軟です。ただし、オブジェクトグラフ全体に広がる依存関係のもつれを管理するためにIoCコンテナーが必要になるほど、設計が複雑になる場合があります。
DIコンテナーをサービスロケーターとして使用することは、実際にはアンチパターンであり、広く議論されています。時々これをしなければなりませんが、一般的に言えばこれを避けるべきです。基本的に、container.Resolveを呼び出すことにより、IoCを実行していません。IoCは、DIコンテナーが実行しようとしていることです。
実際にDIコンテナーを使用するのは、必要に応じて依存関係を注入することです。アプリケーションを初期化/開始するときに、インターフェースを実装するクラスを事前に登録し、そこにすべての詳細を指定します-単純な初期化、パラメーターの使用、ファクトリーの使用など、ライフスコープの指定など。コンストラクターがパラメーターを持っている限り、実際に気にする必要はありません。登録されたクラスを注入できます。だからこれをするとき
もちろん、メインアプリケーションクラスを解決するブートストラップを用意する必要がありますが、このクラスにはすでにパラメーターが挿入されているため、残りはすべて自動的に実行されます。 ISession、ICustomerRepository、またはIWhatEverElseYouNeedが必要な場合は、必要になるたびにインスタンス化することを心配することなく取得できます。
問題は、依存関係の具体的な実装が必要な知識はどこにあるか、そしてこの依存関係に必要なセットアップ(構成や他の依存関係など)を最適に保つことです。これは依存オブジェクトにある場合とない場合があります。いつものように、答えは変わります。しかし、他の依存関係や、URLやパスワードなどの構成データを必要とする複雑なオブジェクトについて考えてみましょう。この依存関係は、他のオブジェクトの依存関係である可能性もあります。この場合、セットアップの知識は依存オブジェクトに配置しないでください。冗長であったり、クラスの関係が煩雑であったりするため、DIコンテナを使用する場合よりも全体的な複雑さが増し、この知識が1か所に保持されます。 。
もちろん、このケースのファクトリを実装することもできます。しかし、アプリケーションに複雑な依存関係がたくさんあり、それらにすべて何らかの種類のファクトリが必要な場合はどうでしょう。これで、工場が浮かんでいて、アプリケーションの依存関係がどのように相互接続されているかに関する知識の一部がすべて含まれているという問題があります。この場合にコンテナを使用すると、システムの透明性と透明性が大幅に向上します。このレベルのDIコンテナは、工場の工場としてよく使用されます。
アプリケーションが複雑になるほど、パーツを相互に接続する中心的な場所を設けることが有用になります。