3

私は MVP パターンを使用している Visual Studio .NET 4.0 で Windows アプリケーションを開発しており、アプリケーション全体で依存性注入の原則を学習して適用しようとしています。しかし、特定の状況に対処する最善の方法を理解するのに苦労しています. 長い説明を前もってお詫びしますが、それ以外の方法で十分に具体的に説明できるかどうかはわかりません.

このアプリケーションは、さまざまな種類の記録に対応する汎用の記録管理アプリであり、相互に関連するものもあれば、他の記録とは無関係のものもあります。私たちが扱うそれぞれの異なるレコードタイプは、アプリケーションの個別の「機能」を構成します。したがって、アプリケーションの起動画面は基本的に、ナビゲートしたい機能を選択できるスイッチボードです。

選択すると、その機能のウィンドウ (ビュー) が開き、このウィンドウからその機能に関するさまざまな操作を実行できます。ほとんどの機能 (顧客、注文など) には、ユーザーが実行できる一般的な操作 (新しいレコードの作成、既存のレコードを開く、変更の保存、印刷など) があります。ただし、その 1 つの機能に固有の操作を持つものもあります (注文機能に固有の検証操作など)。私の問題は、各機能内で操作を実行する方法を理解することにあります。

私は MVP を使用しているため、機能ウィンドウには、ユーザーがその機能の操作を開始したときに応答するプレゼンターがあります。しかし、私は単一の責任の原則に従いたいので、プレゼンターが神のクラスになり、実際にレコードの作成、保存、印刷などの方法を知ってほしくないので、私の考えは、各操作を処理するために別々のクラスを定義することでした。 .Create() メソッドを使用した ICreateOrders 実装、.Save() メソッドを使用した ISaveOrders 実装などがあります。それらに名前を付けるために、実際にリクエストを解決するため、「リゾルバー」と呼びます。その操作が行われるようにします。これらの一般的なリゾルバーのいくつかは、おそらく基本実装 (基本的な FeatureSave クラスなど) を持っているでしょう。ただし、必要に応じて、各機能に特定の実装を含めることができる必要があります。このアプローチには、いくつかの疑問が生じます。

まず、これらのアクションを、その機能のウィンドウだけでなく、アプリ内の他の場所から開始できるようにしたいと考えています。たとえば、注文機能ウィンドウ内からのみ注文レコードを開くことができるのではなく、顧客機能から「注文の表示」を選択して、その顧客の注文レコードを開いて自動的に表示できるようにしたいと考えています。そして、コードの再利用を実現するために、アプリ内のどこからリクエストが開始されたかに関係なく、同じ IOpenOrders 実装を使用してジョブを実行したいと考えています。そのため、必要なリゾルバーを [注文] ウィンドウのプレゼンターに埋め込むだけでは、選択肢のようには思えません。

そこで、Feature オブジェクトのコレクションを保持する FeatureService クラスを作成することを考えました。アプリの起動時に、ログインしているユーザーに有効なすべての機能のリストをデータベースから取得し、そのリストを繰り返し処理して、それぞれの Feature オブジェクトを作成し、FeatureService のコレクションに追加します。次に、ある機能の操作を別の機能から開始する必要がある場合は、FeatureService オブジェクトをソース クラスに挿入し、そこからターゲット機能を取得し、それを使用して操作を実行するだけで実現できます。しかし、Feature クラスを God クラスにしたくないので、これは単に、個々のリゾルバーをすべて Orders プレゼンターから Feature クラスに移動することになります。

しかし、これを実現するには、ユーザーがそれらの操作を実行するかどうかを知らずに、Feature オブジェクトに必要な可能性のあるすべてのリゾルバーを挿入する必要があります。より複雑な機能の一部では、これは 20 または 30 の異なるリゾルバーが注入される可能性があり、これは確かにコンストラクター注入の悪用のようです。これにより、Feature オブジェクトが God クラスになることはありませんが、注入されたリゾルバーに各操作を渡すだけなので、これは間違っているようです。オブジェクトがこのように「神の調整者」になる場合、これは正しい使用法ですか?

私の他の質問は、これらのオブジェクトのボリュームについてです。顧客によっては、50 以上の個別の機能を利用できる場合があります (時間の経過とともにさらに多くの機能が追加されることは間違いありません)。したがって、起動時にこれらの Feature オブジェクトを作成し、コンテナーがこれらすべてのリゾルバー依存関係をそれぞれに注入しているため、それらの機能の一部にアクセスするかどうかに関係なく、何百ものオブジェクト (たとえば、機能ごとに 40 個の機能 X 20 個のリゾルバー) を作成しています。いいえ。典型的なユーザー セッションでは、おそらくいくつかの機能を使用するだけなので、このように起動時に非常に多くのオブジェクトを作成するのは無駄に思えます。あきらめて、Feature オブジェクトに内部的に ServiceLocation を実行させてリゾルバーを取得することもできますが、より良い方法があればそれを使用したいと思います。

ここからどこへ行けばいいのかわかりません。これに関する例と情報を探して見つけようとしましたが、何を検索すればよいかを知るのに十分な知識があるかどうかさえわかりません。誰でも提供できるガイダンスや洞察は大歓迎です。

4

1 に答える 1

2

私の理解が正しければ、 を使用してビルドしようとしているICreateOrdersISaveOrdersリポジトリ パターンです。

通常、リポジトリは次のようになります。

public interface IRepository<TEntity>
{
    TEntity GetByKey(int key);

    TEntity CreateNew();

    void Save(TEntity entity);

    void Delete(TEntity entity);
}

しかし、これを実現するには、ユーザーがそれらの操作を実行するかどうかを知らずに、Feature オブジェクトに必要な可能性のあるすべてのリゾルバーを挿入する必要があります。より複雑な機能の一部では、これは 20 または 30 の異なるリゾルバーが注入される可能性があり、これは確かにコンストラクター注入の悪用のようです。

リポジトリをグループ化するためのパターンがあります。これはUnit of Work パターンと呼ばれます。

作業ユニットはビジネス トランザクションを使用し、変更の書き込みを調整し、それが保持するリポジトリへのアクセスを許可する場合があります。

通常、コンシューマが多くのリポジトリを必要とする場合は、IUnitOfWork代わりに を注入します。それでも、これはリポジトリを作業単位に挿入するという問題を解決するだけです。この問題は、作業ユニットをファクトリとして実装するか、リポジトリ ファクトリを作業ユニットに注入することで解決できます。

public interface IRepositoryFactory
{
     IRepository<TEntity> CreateRepository<TEntity>();
}

の実装を作成するのIRepositoryFactoryは簡単です。これは、DI コンテナーに直接マップされるためです。

private sealed class FrameworkSpecificRepositoryFactory
    : IRepositoryFactory
{
     private readonly Container container;

     public FrameworkSpecificRepositoryFactory(Container container)
     {
         this.container = container;
     }

     public IRepository<TEntity> CreateRepository<TEntity>()
     {
         return this.container.GetInstance<IRepository<TEntity>>();
     }
}

この実装では、DI コンテナーを直接使用します。Service Locator anti-patternの形式のように見えますが、これはこのクラスが配置されている場所によって異なります。この実装がComposition Rootの一部である場合、この実装はインフラストラクチャ コンポーネントであり、その場合は問題ありません。秘訣は、ビジネス ロジックをインフラストラクチャ コンポーネントから遠ざけることです。

ps次回は質問を短くするようにしてください。質問が短いほど、回答が得られる可能性が高くなります。

于 2012-11-03T10:37:02.373 に答える