メソッドインジェクション
操作ごとに依存関係が異なる場合、どのように依存関係をクラスに埋め込むことができますか?
メソッドにパラメーターとして渡すことにより。 各メソッド呼び出しで異なる依存関係が使用されている場合は、メソッドパラメーターを介して渡すことができます。
仕組み
呼び出し元は、呼び出されるたびに依存関係をパラメーターとしてメソッドに渡します。 この手順は、以下のメソッドのシグネチャよりも複雑ではありません。
public void DoStuff(ISomeInterface dependency)
多くの場合、依存関係は、対応する値として渡される操作のコンテキストの種類を表します。
public string DoStuff(SomeValue value, ISomeContext context)
サービスが依存関係を使用する場合、最初にnullをチェックする必要があります 。
メソッド実装を使用する場合
メソッドの実装は、メソッドへの各呼び出しが異なる依存関係を設定するときに最適に使用されます。 これは、中毒自体に価値がある場合に発生する可能性があります。
コンストラクターやプロパティではなく、メソッドを介して依存関係を渡す方が適切な場合がいくつかあります。
- メソッドは静的であり、他のオプションは適切ではありません。 同じコンテキストで、 IFormatProviderはdouble.Parseメソッドおよび他の同様のメソッドで使用されます。
- 依存関係は、操作ごとに異なる場合があります。 ストラテジーパターンのバリアントがあります。このストラテジーはコンストラクター引数に渡すことができません。1つのメソッドでのみ必要であり、呼び出しごとに変更できるためです。 このような戦略の典型的な例は、 List <T> .Sort()メソッドに渡されるソート戦略です。 オブジェクトの作成場所ではなく、操作呼び出しの場所で何らかの戦略が利用可能な場合、同じアプローチを適用できます。
- 操作を実行するためにローカルコンテキストを渡します。 Team 、 Statusなどの多くの設計パターンでは、追加の外部コンテキストを使用して操作を実行できます。 同じ方法は、呼び出しコードに既知の追加コンテキストがストリーム (またはタイマー)に転送されるときに、 マルチスレッドプログラミングで集中的に使用されます。
使用例
public interface ICommandContext { int ProcessorCount { get; } } // CustomCommand public void Execute(ICommandContext context) {} IFormatProvider provider = new NumberFormatInfo { NumberDecimalSeparator = ";" }; // "" double var value = double.Parse("1;1", provider); IComparer<int> comparer = Comparer<int>.Default; var list = new List<int> {3, 4, 1}; // "" list.Sort(comparer); var task = Task.Run(() => { }); TaskScheduler taskScheduler = TaskScheduler.Current; // "" "" task.ContinueWith(t => { }, taskScheduler);
おわりに
メソッドインジェクションによる実装は 、依存関係管理のコンテキストでは非常に一般的なパターンではありませんが、これはライブラリで非常に一般的なアプローチであり、追加のコンテキストまたは戦略を操作から操作に操作にドラッグするためのいくつかの設計パターンです。
周囲の状況
各コンポーネントAPIにアプリケーションの横断的な側面を含めることなく、各モジュールで依存関係を使用可能にするにはどうすればよいですか?
周囲のコンテキストは、静的プロパティまたはメソッドを介してすべてのコンシューマーにアクセス可能です。 消費クラスでは、次のように使用できます。
public string GetMessage() { return SomeContext.Current.SomeValue; }
依存性注入シナリオで役立つようにするには、コンテキスト自体が抽象化されている必要があります。 同時に、外部から変更できるようにする必要があります-これは、Currentプロパティが値の書き込みを許可することを意味します( 書き込み可能 )。
public abstract class SomeContext { public static SomeContext Current { get { if (Thread.GetData(Thread.GetNamedDataSlot("SomeContext")) is SomeContext ctx) return ctx; ctx = Default; Thread.SetData(Thread.GetNamedDataSlot("SomeContext"), ctx); return ctx; } set => Thread.SetData(Thread.GetNamedDataSlot("SomeContext"), value); } private static SomeContext Default = new DefaultContext(); public abstract string SomeValue { get; } }
周囲のコンテキストの構造は、アンチパターンサービスロケーターに似ています。 それらの違いは、周囲のコンテキストが単一の強く型付けされた依存関係のインスタンスのみを提供するのに対し、 Latitudeサービスは要求した依存関係のインスタンスを提供することです。
周囲のコンテキストを使用する場合
- コンテキストリクエストを行う機能が必要です。 データのみを要求する必要がある場合(たとえば、現在の時刻)
- 適切なローカルデフォルトが定義されています。 暗黙的に使用されるため、コンテキストが正しく機能することが重要です。
- 保証された可用性が必要です。 適切なローカルデフォルトが定義されている場合でも、 nullに設定されないように保護する必要があります 。
重要な機能
長所 | 欠点 |
汚染なしAPI | 暗黙的 |
常に利用可能 | 適切な実装を取得するのは難しい |
- | 一部のランタイム環境では正常に動作しない場合があります |
暗黙的 周囲のコンテキストで作業する場合、このコンテキストが特定のクラスで使用されているかどうかを確実に確認することはできません。
実装の複雑さ。 周囲のコンテキストの正しい実装で十分な場合があります。 少なくとも、コンテキストが常に使用可能な状態であることを確認する必要があります。つまり、コンテキストの1つの実装が別の実装に置き換えられずに削除されたからといって、 NullReferenceExceptionsのような例外的な状況はありません。
ASP.NETのパフォーマンスの問題。 周囲のコンテキストがTLSを使用する場合、 ASP.NETでアプリケーションを実行するときに問題が発生する可能性があります。これは、Webページのライフサイクルの特定の時点でフローが変更される可能性が高いためです。 TLSに保存されたデータが古いストリームから新しいストリームにコピーされることは保証されません。 このような状況では、特定の要求データを保存するために、 TLSではなく現在のHttpContextを使用する必要があります。
既知の用途
- セキュリティは、各スレッドに関連付けられたSystem.Security.Principal.IPrincipalインターフェイスを通じて実装されます。 Thread.CurrentPrincipalのアクセスメソッドを使用して、スレッドの現在の所有者を取得( 取得 )または設定( 設定 )できます。
- Thread.CurrentCultureおよびThread.CurrentUICultureを使用すると、現在の操作の文化的コンテキストにアクセスして変更できます。 パーサーや型コンバーターなどの多くのフォーマットAPIは 、他に明示的に設定されていない場合、現在のカルチャを暗黙的に使用します。
- Traceクラスは特定のスレッドに関連付けられていませんが、アプリケーション全体で共有されています。 Trace.Writeメソッドを使用すると、どこからでもトレースメッセージを記録できます。
おわりに
元のインターフェースに含まれていない応答を受信するために、横断的なアスペクト依存性を要求する必要がある場合、 ローカルdefaultがある場合、 周囲のコンテキストを使用できます 。 したがって、明示的な構成を行わなくても、実際のコンテキストとすべてのクライアントで使用されるデフォルトの動作を組み合わせることができます。
この記事は、依存性注入パターンで終わります。 コンストラクターを介した実装、プロパティを介した実装、メソッドを介した実装、および周囲のコンテキストなど、4つの依存性注入パターンを検討しました。 特定のタスクに選択するテンプレートを決定するために、 Mark Simanが提案する適切なパターンを選択するアルゴリズムを以下に示します 。 できるだけ頻繁に使用してください。 そして、このアルゴリズムが役に立たない場合は、コンストラクターの実装を選択してください-この場合、ひどい間違いをすることはありません。