私はあなたのポイントを数値で調べますが、最初に、あなたが非常に注意しなければならないことがあります:消費者がライブラリを使用する方法とライブラリの実装方法を混同しないでください。これの良い例は、Entity Framework(あなた自身が優れたライブラリとして引用している)とASP.NETのMVCです。これらの両方は、たとえば、リフレクションを使用して非常に多くのことを行います。これは、日常のコードに拡散すると、絶対に良いデザインとは見なされません。
これらのライブラリは、その動作方法や舞台裏で行われていることにおいて「透過的」ではありません。しかし、消費者の優れたプログラミング原則をサポートしているため、それは不利益ではありません。したがって、このようなライブラリについて話すときはいつでも、ライブラリの消費者として、その実装やメンテナンスを心配するのはあなたの仕事ではないことを忘れないでください。ライブラリを使用するコードを作成するのにどのように役立つか、またはそれを妨げるかについてのみ心配する必要があります。これらの概念を混同しないでください!
だから、ポイントで行くには:
すぐに上記の例であると推測できるものに到達します。IOCコンテナはほとんどの依存関係を静的として設定すると言います。おそらく、それらがどのように機能するかの実装の詳細には、静的ストレージが含まれます(NinjectのようなオブジェクトのインスタンスをIKernel
コアストレージとして持つ傾向があることを考えると、それさえ疑問です)。しかし、これはあなたの関心事ではありません!
実際、IoCコンテナーは、貧乏人の依存性注入と同じようにスコープで明示的です。(IoCコンテナを貧乏人のDIと比較し続けるのは、DIをまったくDIと比較することは不公平で混乱を招くためです。そして、明らかに、「貧乏人のDI」を軽jorとして使用していません。)
貧乏人のDIでは、依存関係を手動でインスタンス化し、それを必要とするクラスに注入します。依存関係を構築する時点で、それをどう処理するかを選択します。ローカルスコープの変数、クラス変数、静的変数に保存します。何も保存しないでください。同じインスタンスを多数のクラスに渡すことも、クラスごとに新しいインスタンスを作成することもできます。なんでも。ポイントは、どちらが起こっているかを確認するために、依存関係が作成されるポイント(おそらくアプリケーションルートの近く)に注目することです。
IoCコンテナについてはどうでしょうか?まあ、あなたはまったく同じことをします!ここでも、Ninjectの用語で行く、あなたはバインディングが設定されているところを見て、それが何かのように述べているかどうかを見つけるInTransientScope
、InSingletonScope
または何でも。何らかのオブジェクトに何が起こるかを追跡するためにメソッドを調べる必要はなく、スコープを宣言するコードがあるため、これが潜在的に明確である場合(オブジェクトはブロックにスコープされているかもしれませんが、その中で複数回使用されていますたとえば、ブロックまたは1回だけ)。したがって、生の言語機能ではなく、IoCコンテナの機能を使用して範囲を決定する必要があるという考えに反発されるかもしれませんが、IoCライブラリを信頼している限り(そうすべきです!) 。
ここであなたが何について話しているのか、まだよくわかりません。IoCコンテナーは、内部実装の一部としてプライベートプロパティを参照しますか?なぜそうなるのかは分かりませんが、もしそうなら、あなたが使用しているライブラリがどのように実装されるかはあなたの関心事ではありません。
または、多分、彼らはプライベートセッターに注入するような機能を公開しますか?正直なところ、私はこれに遭遇したことがなく、これが本当に一般的な機能であるかどうか疑問に思っています。しかし、たとえそれが存在していても、誤用される可能性のあるツールの単純なケースです。IoCコンテナがなくても、プライベートプロパティにアクセスして変更するのはほんの数行のReflectionコードにすぎないことを忘れないでください。それはほとんど絶対にすべきでないことですが、それは.NETが機能を公開するのに悪いことを意味しません。誰かがツールを明白かつ乱暴に誤用した場合、それはツールの責任ではなく、人の責任です。
ここでの最終的なポイントは2に似ています。ほとんどの場合、IoCコンテナーはコンストラクターを使用します。セッターインジェクションは、特定の理由により、コンストラクターインジェクションを使用できない非常に特殊な状況で提供されます。渡される依存関係の数を隠蔽するために常にセッターインジェクションを使用する人はだれでも、ツールを大幅に中止しています。それはツールのせいではなく、彼らのせいです。
さて、これが非常に簡単な間違いであり、IoCコンテナが推奨する間違いである場合、それで問題ありません。それは私のクラスのすべてのメンバーを公開し、他の人がすべきでないものを変更したときに非難するようなものですよね?しかし、SRPの違反を隠蔽するためにセッターインジェクションを使用する人は、基本的な設計原則を故意に無視するか、まったく知らないかのいずれかです。IoCコンテナーでこれを非難するのは不合理です。
それがあります特に、それはまた、あなたが貧乏人のDIと同様に簡単に行うことができるものだから真:
var myObject
= new MyTerriblyLargeObject { DependencyA = new Thing(), DependencyB = new Widget(), Dependency C = new Repository(), ... };
本当に、この心配は、IoCコンテナーが使用されているかどうかに完全に直交しているように見えます。
クラスの連携方法の変更は、OCPの違反ではありません。もしそうなら、すべての依存関係の逆転はOCPの違反を助長するでしょう。これが当てはまる場合、それらは両方とも同じ固い頭字語ではありません!
さらに、a)とb)のどちらもOCPに関係するものではありません。OCPに関してこれらに答える方法すら知りません。
私が推測できる唯一のことは、OCPは、実行時に変更されない動作、または依存関係のライフサイクルがコードのどこから制御されるかに関するものだと思うということです。そうではありません。OCPでは、要件が追加または変更されたときに既存のコードを変更する必要はありません。それはすべて、コードを書くことであり、すでに書いたコードをどのように結び付けるかではありません(もちろん、疎結合はOCPを達成するための重要な部分です)。
そして最後にあなたが言うこと:
しかし、コンパイルされたコードを変更して、手動で実行できないことに対する回避策を実行するサードパーティ製のツールに同じ信頼を置くことはできません。
はい、できます。膨大な数のプロジェクトに依存しているこれらのツールが、他のサードパーティライブラリよりもバグが多いか、予期しない動作を起こしやすいと考える理由はまったくありません。
補遺
私はあなたのイントロのパラグラフがいくつかのアドレス指定も使用できることに気づきました。IoCコンテナは、依存関係グラフを作成するための乱雑で重複しやすいコードを回避するために、「聞いたこともない秘密の手法を採用していない」と皮肉を言っています。そして、あなたはまったく正しいです。彼らがしていることは、私たちプログラマーがいつもしているのと同じ基本的なテクニックでこれらの事柄に実際に取り組んでいるということです。
仮定のシナリオを通して話させてください。プログラマーとして、大規模なアプリケーションを作成し、オブジェクトグラフを作成するエントリポイントで、非常に厄介なコードがあることに気づきます。何度も何度も使用されるクラスは非常に多く、そのうちの1つを構築するたびに、その下にある依存関係のチェーン全体を構築する必要があります。さらに、依存関係のライフサイクルを宣言または制御する表現方法はありませんが、それぞれのカスタムコードを使用する場合を除きます。コードは構造化されておらず、繰り返しに満ちています。これは、イントロの段落で説明する混乱です。
そのため、最初に、リファクタリングを開始します。いくつかの繰り返しコードが十分に構造化されているので、それをヘルパーメソッドに引き出します。しかし、あなたは考え始めます-これはおそらく私が一般的な意味で取り組むことができる問題であり、この特定のプロジェクトに固有のものではなく、将来のすべてのプロジェクトであなたを助けることができる問題ですか?
したがって、あなたは座って考えて、依存関係を解決できるクラスがあるべきだと判断します。そして、必要なパブリックメソッドをスケッチします。
void Bind(Type interfaceType, Type concreteType, bool singleton);
T Resolve<T>();
Bind
「タイプのコンストラクター引数が表示される場所interfaceType
で、インスタンスを渡します」と言いますconcreteType
。追加のsingleton
パラメーターは、concreteType
毎回同じインスタンスを使用するか、常に新しいインスタンスを作成するかを示します。
Resolve
単純に、T
以前にバインドされたすべての型の引数を持つ引数を見つけることができるコンストラクタで構築しようとします。また、依存関係を完全に解決するために、それ自体を再帰的に呼び出すこともできます。すべてがバインドされていないためにインスタンスを解決できない場合、例外をスローします。
これを自分で実装してみてください。少しのリフレクションが必要で、バインディングのキャッシングが必要な場合がありますが、singleton
劇的なことや恐ろしいことはありません。そして、完了したら、あなたはあなた自身のIoCコンテナの中核を手に入れました!それは本当にそんなに怖いですか?これとNinject、StructureMap、Castle Windsor、またはあなたが好むものとの唯一の本当の違いは、これらの基本的なバージョンでは不十分な(多くの!)ユースケースをカバーするためのより多くの機能があることです。しかし、基本的には、IoCコンテナーの本質があります。
IOC
、Explicitness of code
まさに私が問題を抱えているものです。手動DIは簡単に面倒になりますが、少なくとも自己完結型の明示的なプログラムフローを1つの場所に配置します。IOCコンテナのバインディングと宣言は、それ自体で簡単に非表示の並列プログラムになります。