依存性注入パターン。 パート2

前回は 、最も一般的に使用されている基本的な依存性注入パターン調べました。 今日は、柔軟なシステムの設計にも使用される他の2つを分析します。 今日は、メソッドによる実装と周囲のコンテキストについて説明します。 行こう!



メソッドインジェクション



操作ごとに依存関係が異なる場合、どのように依存関係をクラスに埋め込むことができますか?

メソッドにパラメーターとして渡すことにより。 各メソッド呼び出しで異なる依存関係が使用されている場合は、メソッドパラメーターを介して渡すことができます。



仕組み



呼び出し元は、呼び出されるたびに依存関係をパラメーターとしてメソッドに渡します。 この手順は、以下のメソッドのシグネチャよりも複雑ではありません。



public void DoStuff(ISomeInterface dependency)
      
      





多くの場合、依存関係は、対応する値として渡される操作のコンテキストの種類を表します。



 public string DoStuff(SomeValue value, ISomeContext context)
      
      





サービスが依存関係を使用する場合、最初にnullをチェックする必要があります



メソッド実装を使用する場合



メソッドの実装は、メソッドへの呼び出しが異なる依存関係を設定するときに最適に使用されます。 これは、中毒自体に価値がある場合に発生する可能性があります。



コンストラクターやプロパティではなく、メソッドを介して依存関係を渡す方が適切な場合がいくつかあります。





使用例



 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サービスは要求した依存関係のインスタンスを提供することです。



周囲のコンテキストを使用する場合





重要な機能



長所 欠点
汚染なしAPI 暗黙的
常に利用可能 適切な実装を取得するのは難しい
- 一部のランタイム環境では正常に動作しない場合があります




暗黙的 周囲のコンテキストで作業する場合このコンテキストが特定のクラスで使用されているかどうかを確実に確認することはできません。



実装の複雑さ。 周囲のコンテキストの正しい実装で十分な場合があります。 少なくとも、コンテキストが常に使用可能な状態であることを確認する必要があります。つまり、コンテキストの1つの実装が別の実装に置き換えられずに削除されたからといって、 NullReferenceExceptionsのような例外的な状況はありません。



ASP.NETのパフォーマンスの問題。 周囲のコンテキストTLSを使用する場合、 ASP.NETでアプリケーションを実行するときに問題が発生する可能性があります。これは、Webページのライフサイクルの特定の時点でフローが変更される可能性が高いためです。 TLSに保存されたデータが古いストリームから新しいストリームにコピーされることは保証されません。 このような状況では、特定の要求データを保存するために、 TLSではなく現在のHttpContextを使用する必要があります。



既知の用途





おわりに



元のインターフェースに含まれていない応答を受信するために、横断的なアスペクト依存性を要求する必要がある場合、 ローカルdefaultがある場合、 周囲のコンテキストを使用できます したがって、明示的な構成を行わなくても、実際のコンテキストとすべてのクライアントで使用されるデフォルトの動作を組み合わせることができます。



この記事は、依存性注入パターンで終わります。 コンストラクターを介した実装、プロパティを介した実装、メソッドを介した実装、および周囲のコンテキストなど、4つの依存性注入パターンを検討しました。 特定のタスクに選択するテンプレートを決定するために、 Mark Simanが提案する適切なパターンを選択するアルゴリズムを以下に示します 。 できるだけ頻繁に使用してください。 そして、このアルゴリズムが役に立たない場合は、コンストラクターの実装を選択してください-この場合、ひどい間違いをすることはありません。










All Articles