3

数十の「高レベル」インターフェイス (コンポーネント レベルを意味する) を実装する数百のクラスを持つアプリケーションがあるとします。依存性注入を行うための推奨される方法はありますか (unity のように)。シングルトンとしてアクセス可能な、ブートストラップに使用できる「一般的なコンテナ」が必要ですか? すべてのインスタンスが RegisterInstance できるコンテナを渡す必要がありますか? スタートアップのどこかで RegisterType によってすべてを行う必要がありますか? 必要なときにコンテナにアクセスできるようにする方法。コンストラクター インジェクションは間違っているように見えます。標準的な方法であることに反論があります。インターフェイスをコンポーネント レベルから起動時にすぐに使用される一番下まで渡す必要があるか、参照が保持されて「どこに住んでいるかを知る」アンチパターンになってしまいます。 . そして:コンテナを「利用可能」にする 開発者は、サーバー コンポーネントをクライアント コンテキストで解決するという考えにたどり着く可能性があります。それを避ける方法は?

どんな議論でも大歓迎です!


明確にするために編集

私が見ている問題をよりよく理解するために、やや現実的な例を見つけました。

アプリケーションがハイファイ システムであると想像してみましょう。システムには、音楽を再生するための CD プレーヤー (CD ラックに統合) と USB ポート (USB ラックに統合) があります。

これで、CD プレーヤーと USB ポートで mp3 音楽を再生できるようになります。

私は注射可能なmp3デコーダーをどこかに持っています。

今、私はハイファイシステムを開始します。まだ CD が挿入されておらず、USB スティックが接続されていません。mp3 デコーダーは今は必要ありません。

しかし、コンストラクター注入では、CD ラックと USB ラックに依存関係を既に注入する必要があります。

mp3 CD や mp3 USB スティックを挿入しないとどうなりますか?

リファレンスを CD ラックに保持して、MP3 CD が挿入されたときにデコーダーを手元に用意しますか? (私には間違っているようです)

デコーダーは、mp3 が挿入された場合にのみ開始される cd ラックのサブシステムで必要です。CD ラックにコンテナーがありません。ここでコンストラクター インジェクションはどうですか?

4

4 に答える 4

6

まず、依存性注入はコンテナーを必要としない設計パターンです。DI パターンは次のように述べています。

依存性注入は、コンパイル時ではなく実行時にコンポーネントの選択を可能にするソフトウェア設計パターンです。

たとえば、Guice (Java 依存性注入フレームワーク) を取り上げます。Java では、Guice は DI フレームワークですが、それ自体はコンテナーではありません。

.Net のほとんどの DI ツールは実際にはコンテナーであるため、依存関係を注入できるようにコンテナーにデータを入力する必要があります。

コンテナに毎回すべてのコンポーネントを登録しなければならないという考えは好きではありません。単純に嫌いです。規則に基づいてコンポーネントを自動登録するのに役立つツールがいくつかあります。私は Unity を使用しませんが、たとえばNinjectAutoFacを紹介できます。

私は実際に、実質的にすべてのDIツールを使用して、慣習に基づいてコンポーネントを自動登録するための小さなユーティリティを作成しています.それはまだ開発段階にあります.

ご質問について:

シングルトンとしてアクセス可能な、ブートストラップに使用できる「一般的なコンテナ」が必要ですか?

答えはイエスです (使用される DI ツールを抽象化するツールがあり、それはServiceLocatorと呼ばれます)、これが DI ツールのしくみです。アプリケーションで利用できる静的コンテナーがありますが、ドメイン オブジェクト内で使用することはお勧めしません。アンチパターンと見なされるインスタンスを作成する

ところで、実行時にコンポーネントを登録するには、このツールが非常に便利であることがわかりました。

http://bootstrapper.codeplex.com/

すべてのインスタンスが RegisterInstance できるコンテナを渡す必要がありますか?

いいえ、デメテルの法則に違反します。コンテナを使用する場合は、アプリケーションの起動時にコンポーネントを登録することをお勧めします

必要なときにコンテナにアクセスできるようにする方法

Common Service Locator を使用すると、アプリケーションのどこでも使用できますが、前述したように、ドメイン オブジェクト内で使用して必要なインスタンスを作成することはお勧めしません。代わりに、オブジェクトのコンストラクターにオブジェクトを挿入して、 DI ツールを使用して、正しいインスタンスを自動的に挿入します。

これに基づいて:

コンストラクター インジェクションは間違っているようで、標準的な方法であることに反論しています。インターフェイスをコンポーネント レベルから起動時にすぐに使用される一番下まで渡す必要があるか、参照が保持されて「どこに住んでいるかを知る」アンチパターンになってしまいます。

アプリケーションの単体テストを大量に作成していないと思われますが、これは悪いことです。したがって、私の提案は、使用するDIツールを選択する前、またはこの質問に対するすべての回答を考慮する前に、次のリンクを参照することです.1つのことに焦点を当てています:あなた自身の質問に答えることができる最高の情報源

クリーンコードの話:

記事

次のリンクは非常に強くお勧めします

http://misko.hevery.com/2008/09/30/to-new-or-not-to-new/

http://www.loosecouplings.com/2011/01/how-to-write-testable-code-overview.html

于 2012-06-12T21:45:47.797 に答える
2

コンテナを回すべきか

いいえ、これはService Locator のアンチパターンにつながるためです。

すべてのインスタンスが RegisterInstance できる場所

サービスは、型自体ではなく、アプリケーションの起動パスに登録する必要があります。複数のアプリケーション (Web アプリ、Web サービス、WPF クライアントなど) がある場合、共有レイヤーのためにすべてのサービスを結び付ける共通のブートストラップ プロジェクトが存在することがよくあります (ただし、各アプリケーションには固有の配線があります。同じ)。

スタートアップのどこかで RegisterType によってすべてを行う必要があります

はい、起動時にすべてを配線する必要があります。

必要なときにコンテナにアクセスできるようにする方法。

すべきではありません。アプリケーションは、コンテナーの使用を意識する必要があります (コンテナーが使用されている場合、これはオプションであるため)。これを行わないと、テストなど、多くのことがさらに困難になります。ただし、アプリケーションのスタートアップ パス (コンポジション ルートとも呼ばれます) で定義されている型にコンテナーを挿入することはできます。このようにして、アプリケーションはコンテナーについて何も知らない状態を保ちます。

コンストラクターの注入は間違っているようで、標準的な方法であることに異議を唱えています

コンストラクター注入は、依存関係を注入するための推奨される方法ですただし、既存のアプリケーションをコンストラクター インジェクションに向けてリファクタリングするのは困難な場合があります。コンストラクター注入が機能しない **まれな* 状況では、プロパティ注入に戻すか、完全なオブジェクト グラフを構築することが不可能な場合は、ファクトリを注入できます。ファクトリの実装がコンポジション ルートの一部である場合は、コンテナーに依存させることができます。

依存性注入パターンとSOLID設計原則の上に構築できる非常に便利なパターンは、コマンド/ハンドラー パターンです。これは小さなアプリでは便利なパターンですが、エンタープライズ アプリケーションのようにアプリケーションが大きくなると、その効果が発揮されます。

于 2012-06-13T08:44:44.707 に答える
2

まず、コンポジション ルート パターンがあります。依存関係をできるだけ早くセットアップします。デスクトップでは Main()、Web では global.asax/app_start です。次に、依存関係が明確になるため、コンストラクター注入に依存します。

ただし、実際に依存関係を解決するには、まだ何かが必要です。私が知っているアプローチは少なくとも 2 つあります。

1 つ目は、Service Locator を使用することです (これは、コンテナーをシングルトンにすることとほぼ同じです)。これは多くの人にとってアンチパターンと考えられていますが、うまく機能します。サービスが必要なときはいつでも、コンテナーに要求します。

 ... business code...
 var service = ServiceLocator.Current.GetInstance<IMyService>();
 service.Foo();  

Service Locator は、Composition Root で設定したコンテナーを使用するだけです。

別のアプローチは、コンテナーが注入されたシングルトンとして利用可能なオブジェクト ファクトリに依存することです。

 var service = IMyServiceFactory.Instance.CreateService();

秘訣は、ファクトリの実装がコンテナを内部的に使用してサービスを解決することです。ただし、このアプローチでは、追加のファクトリ レイヤーを使用すると、ビジネス コードが独立し、IoCを認識しなくなります。ファクトリを完全に再設計して、内部で IoCを使用せず、ビジネス コードのすべての行を維持することができます。

つまり、コンテナ/ロケータをファクトリ レイヤの後ろに隠すのは単なるトリックです。

前者のアプローチ (ロケーターに直接依存する) を使用する誘惑に駆られますが、私は後者を好みます。すっきりした感じです。

ここに他の実行可能な代替手段があるかどうか疑問に思います。

于 2012-06-12T22:00:03.003 に答える
1

今、私はハイファイシステムを開始します。まだ CD が挿入されておらず、USB スティックが接続されていません。mp3 デコーダーは今は必要ありません。

これは、 Setter インジェクション(=C# でのプロパティ インジェクション) に最適なようです。NeutralDecoder または NullDecoder をデフォルトとして使用し、必要に応じて Mp3Decoder を挿入します。手動で、または DI コンテナーと条件付き/遅延バインディングを使用して行うことができます。

http://blog.springsource.com/2007/07/11/setter-injection-versus-constructor-injection-and-the-use-of-required/

通常、すべての 必須のコラボレーターにはコンストラクター注入を使用し、他のすべてのプロパティにはセッター注入を使用することをお勧めします。

于 2012-06-13T09:38:57.447 に答える