これは SO のポリシーに少し反しますが、Simon の回答を拡張するために、Mark Seeman の優れたブログ投稿を紹介します
: http://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/ブログ投稿へのコメントも非常に興味深いものです。
ninject と拡張機能に関する問題に対処するために、それらを作成した時点であなた/コンポジション ルートには知られていないと思いますNinjectModule
-s を指摘したいと思います。Ninject はすでに、すべての dll で頻繁に使用される拡張メカニズムを備えていninject.extension.XYZ
ます。
あなたがすることはFooExtensionModule : NinjectModule
、拡張機能にいくつかのクラスを実装することです。モジュールにはBind
メソッドが含まれています。次に、いくつかの .dll からすべてのモジュールをロードするように ninject に指示します。それでおしまい。
ここでは、はるかに詳細に説明されています: https://github.com/ninject/ninject/wiki/Modules-and-the-Kernel
欠点:
- 拡張機能は Ninject に依存します
- 「ninjectを更新する」ときに拡張機能を再コンパイルする必要がある場合があります
- ソフトウェアが成長するにつれて、DI コンテナーを切り替えるコストがますます高くなります。
Rebind
追跡が困難な問題が発生する可能性があります(これは、モジュールを使用しているかどうかに関係なく当てはまります。)
- 特に、拡張機能の開発者が他の拡張機能について知らない場合、同一または競合するバインディング ( や など) を作成する可能性があり
.Bind<string>().ToConst("Foo")
ます.Bind<string>().ToConst("Bar")
。繰り返しますが、これはモジュールを使用していない場合にも当てはまりますが、拡張機能によってさらに複雑なレイヤーが追加されます。
利点: - シンプルで要点を言えば、コンテナを抽象化するために必要な複雑化/抽象化の余分なレイヤーはありません。
私はNinjectModule
、それほど小さくないアプリケーション (15k ユニット/コンポーネント テスト) でこのアプローチを使用して、多くの成功を収めました。
スコープなしなどの単純なバインディングだけが必要な場合は.Bind<IFoo>().To<Foo>()
、クラスに属性を配置し、これらをスキャンし、コンポジション ルートでバインディングを作成するなど、より単純なシステムの使用を検討することもできます。このアプローチはそれほど強力ではありませんが、そのため、はるかに「移植性」が高くなります (他の DI コンテナーでの使用に適応します)。
依存性注入の遅いインスタンス化
コンポジション ルートの考え方は、(可能な限り) すべてのオブジェクト (オブジェクト グラフ全体) を一度に作成することです。たとえば、Main
メソッドでkernel.Get<IMainViewModel>().Show()
.
ただし、これが実行できない場合や適切でない場合もあります。そのような場合、ファクトリを使用する必要があります。実際、stackoverflow には、この点に関する多くの回答があります。次の 3 つの基本的なタイプがあります。
- and (注入された ctor)
Foo
のインスタンスを必要とするインスタンスを作成するには、注入されたandの 1 つのインスタンスを取得するクラスを作成します。その後、メソッドは実行します。Dependency1
Dependency2
FooFactory
Dependency1
Dependency2
FooFactory.Create
new Foo(this.dependency1, this.dependency2)
- ninject.extensions.Factoryを使用します:
Func<Foo>
ファクトリとして使用: をFunc<Foo>
注入し、それを呼び出して のインスタンスを作成できますFoo
。
- インターフェイスと
.ToFactory()
バインディングを使用します (このアプローチをお勧めします。よりクリーンなコード、より優れたテスト容易性)。例: IFooFactory
with method Foo Create()
. 次のようにバインドします。Bind<IFooFactory>().ToFactory();
実装を置き換える拡張機能
私見、これは依存性注入コンテナーの目標の 1 つではありません。それが不可能だという意味ではありませんが、自分で解決しなければならないということです。
ninject を使用する最も簡単な方法は、 を使用すること.Rebind<IFoo>().To<SomeExtensionsFoo>()
です。ただし、前述のように、それは少しもろいです。Bind
とが間違っRebind
た順序で実行されると、失敗します。複数Rebind
の がある場合、最後のものが勝ちますが、それは正しいものですか?
それでは、さらに一歩進めましょう。想像:
`.Bind<IFoo>().To<SomeExtensionsFoo>().WhenExtensionEnabled<SomeExtension>();`
構文メソッドを拡張する独自のカスタムWhenExtensionEnabled<TExtension>()
拡張メソッドを考案できます。When(Func<bool> condition)
拡張機能が有効かどうかを検出する方法を工夫する必要があります。