はじめに
この記事では、依存性注入条件でコンポーネントを実装するためのデータ依存性パターンについて説明します。 例では、C#言語とUnityを使用します。
依存性注入では不十分であり、データ注入に頼る必要がある状況を説明することから始めましょう。
挑戦する
チームのリストに基づいて、
K
評価を最高にするコンポーネントを開発する必要があります。 チームは、開発中のコンポーネントの一部ではなく、DIを介してリンクされている他のさまざまなランキングコンポーネントを通じて評価する必要があります。 チームの推定値として、すべてのレンジャーの平均を取る必要があります。
チーム:
public interface ITeam { string Name { get; } }
ランカー:
public interface IRanker { int Rank(ITeam team); }
評価エントリ:
public interface ITopRecord { ITeam Team { get; } int Position { get; } double AverageRank { get; } }
コンポーネントインターフェイス:
public interface ITopBuilder { IEnumerable<ITopRecord> BuildTop(IEnumerable<ITeam> teams, int topCount); }
使用するコンテナによっては、評価者のプリミティブプロバイダーが追加で必要になる場合があります。
public interface IRankerProvider { IEnumerable<IRanker> GetRankers(); }
実装
実装を開始し、別のアセンブリでインターフェイスを
IRanker
の開発者(
IRanker
)に
IRanker
て、彼らが作業を
IRanker
ようにすることができます。
IRankerProvider
インターフェースの実装は、どのランカーを使用する必要があるかを知っているユーザーが担当するため、
ITopRecord
と
ITopBuilder
のみ
ITopRecord
ITopBuilder
。 レンジャーのコンポジットを作成することもできます。 私たちは、与え、忘れます。
しばらくすると、予期しない問題が表示されます。 ランク付けするには、ランク付け者の1人が合計でいくつのチームがあるかを知る必要があります。 通常、この場所では、不親切で貧弱な建築などがすべて思い出されます。 しばしばい解決策に頼る-レンジャーのインターフェースを変更するには:
public interface IRanker { int Rank(ITeam team,int teamCount); }
したがって、契約を変更すると、必然的にすべての実装に影響します。
この状況では、別のソリューションを好む場合があります。 これは、2つの異なるタイプのランカーの作成で構成されています。
public interface IRanker { int Rank(ITeam team); } public interface IRankerWithCount { int Rank(ITeam team,int teamCount); }
または
ITopBuilder
の実装の変更を必然的に必要とする同様のスキーム。
新しい要件が到着すると、地獄が始まります-別のレンジャーはより多くの情報を必要としました。 彼は、評価対象のチームが偶数位のリストにいるかどうかを知る必要があります。 そして別の人は、ランキングに何人の人がいるかを知る必要があります。
この時点で、give
ITopBuilder
、
ITopBuilder
すべてのパラメーターを
ITopBuilder
渡すのが最も簡単で正確です:
public interface IRanker { int Rank(ITeam team, IEnumerable<ITeam> teams, int topCount); }
コンテキストの概念を導入することで暗黙的にこれを実行し、コンテキストを渡すことができます。 いずれにせよ、必要なデータを抽出する責任は、すべてランカーを実装する人にあり、インターフェースは固定されています。 最初からやるべきだったようです。
別の方法
上記で提案されたソリューションは普遍的ですが、欠点があります。 冗長です。 追加のパラメーターは、不要な場合でもすべてのレンジャーに渡されます。 余分なパラメーターを削除してみましょう。
そして、最初に、追加のパラメーターが依存関係であり、Dependency Injectionパターンとコンテナーがあることに注意してください。
IEnumerable
と
Int32
インスタンスをコンテナに入れるのは悪いことです。 それらを適切なインターフェースでラップします。
public interface ITeamCollection:IEnumerable<ITeam> { } public interface ITopCount { int Count { get; } }
レンジャーの成果物も依存関係として誰かに渡す必要があり、そのインターフェイスを宣言するとします。
public interface ITop : IEnumerable<ITopRecord> { }
ITopBuilder
コンポーネントのインターフェイスは次のようになります。
public interface ITopBuilder { ITop BuildTop(ITeamCollection teams, ITopCount count); }
コンテナを介してパラメータを渡そうとしています。 Unityを使用すると、次のようなメッセージが表示されます。
public ITop BuildTop(ITeamCollection teams, ITopCount count) { using (var container = _container.CreateChildContainer()) { container.RegisterInstance(teams); container.RegisterInstance(count);
ITeamCollection
および
ITopCount
ITeamCollection
を
ITopCount
許可します。 したがって、コードは機能しません。 これは、
IRankerProvider
コンストラクターで
IRankerProvider
許可されており、インスタンスを登録する前に呼び出されるためです。
BuildTop
呼び出すたびに作成される別のオブジェクトに作業を委任することで、この問題を解決できます。
public ITop BuildTop(ITeamCollection teams, ITopCount count) { using (var container = _container.CreateChildContainer()) { container.RegisterInstance(teams); container.RegisterInstance(count); return container.Resolve<BuilerWorker>().DoWork(); } } internal sealed class BuilerWorker { public BuilerWorker( ITeamCollection teams, ITopCount count, IRankerProvider rankerProvider ... ) { } public ITop DoWork() { ... } }
このように、データ依存性という名前を持つために、十分に普遍的で実際に適用できるテンプレートを作成しました。