4

コンストラクターインジェクションの乱用を回避するためのベストプラクティスを探しています。たとえば、以下に示すようなサブエンティティがほとんどない会議エンティティがあります。

  • ミーティング
    1. MeetingContacts
    2. MeetingAttendees
    3. MeetingType
    4. 住所
    5. MeetingCompanies
    6. MeetingNotes

MeetingServiceクラスは次のようになります。

public class MeetingService
{
    private readonly IMeetingContactRepository _meetingContactRepository;
    private readonly IMeetingAttendeeRepository _meetingAttendeeRepository;
    private readonly IMeetingTypeRepository _meetingTypeRepository;
    private readonly IAddressRepository _addressRepository;
    private readonly IMeetingCompanyRepository _meetingCompanyRepository;
    private readonly IMeetingNoteRepository _meetingNoteRepository;
    private readonly IMeetingRepositoy _meetingReposity;

    public MeetingService(IMeetingRepositoy meetingReposity, IMeetingContactRepository meetingContactRepository, IMeetingAttendeeRepository meetingAttendeeRepository, 
        IMeetingTypeRepository meetingTypeRepository, IAddressRepository addressRepository, 
        IMeetingCompanyRepository meetingCompanyRepository, IMeetingNoteRepository meetingNoteRepository)
    {
        _meetingReposity = meetingReposity;
        _meetingContactRepository = meetingContactRepository;
        _meetingAttendeeRepository = meetingAttendeeRepository;
        _meetingTypeRepository = meetingTypeRepository;
        _addressRepository = addressRepository;
        _meetingCompanyRepository = meetingCompanyRepository;
        _meetingNoteRepository = meetingNoteRepository;
    }

    public void SaveMeeting(Meeting meeting)
    {
        meetingReposity.Save();
        if(Condition1())
            _meetingContactRepository.Save();
        if(Condition2())
            _meetingAttendeeRepository.Save();
        if(Condition3())
            _meetingTypeRepository.Save();
        if(Condition4())
            _addressRepository.Save();
        if(Condition5())
            _meetingCompanyRepository.Save();
        if(Condition6())
            _meetingNoteRepository.Save();
    }
    //... other methods
}

ここに7つの依存関係がありますが、実際のコードにはさらに多くの依存関係が含まれています。「依存性注入コンストラクタの狂気」で説明されているさまざまな手法を使用しましたが、リポジトリの依存性を処理する方法が見つかりませんでした。

依存関係の数を減らし、コードをテスト可能に保つ方法はありますか?

4

4 に答える 4

4

コンストラクターの乱用は単なる症状です。メッセージの永続性のさまざまな要素を認識し、それらを全体的な保存にプラグインする「マスター」クラスを用意することで、作業単位を概算しているようです。

欠点は、各リポジトリが専用のSaveメソッドを公開することにより、他のリポジトリの独立性を伝達することです。SaveMeetingただし、リポジトリが独立していないことを明示的に示しているため、これは正しくありません。

リポジトリが消費するタイプを特定または作成することをお勧めします; これにより、変更が一元化され、1か所から保存できます。例としては、DataContext(LINQ to SQL)ISession(NHibernate)、およびObjectContext(Entity Framework)があります。

私の以前の回答で、リポジトリがどのように機能するかについての詳細を見つけることができます。

オブジェクトごとに汎用リポジトリと特定のリポジトリを作成する利点はありますか?

リポジトリを入手したら、それらが機能するコンテキストを特定します。これは通常、単一のWebリクエストにマッピングされます。リクエストの開始時に共通の作業単位のインスタンスを作成し、それをすべてのリポジトリに渡します。リクエストの最後に、作業単位の変更を保存し、リポジトリがアクセスするデータについて心配する必要がないようにします。

これにより、すべてが1つのユニットとしてきちんとキャプチャされ、保存されます。これは、ソース管理システムの作業コピーと非常によく似ています。システムの現在の状態をローカルコンテキストにプルし、それを操作して、完了したら変更を保存します。各ファイルを個別に保存するのではなく、個別のリビジョンとしてすべて同時に保存します。

于 2012-06-17T04:19:57.460 に答える
3

上記の私のコメントを少し拡張するには:

この質問はリポジトリの依存関係を管理する方法に向けられているので、MeetingServiceある種の永続的なコミットを管理していると想定する必要があります。過去に、MeetingServiceこれほど多くの依存関係を持つようなクラスを見たとき、それらがやりすぎていることは明らかです。したがって、「私のトランザクション境界は何ですか」と自問する必要があります。つまり、実行できる最小のコミットは、会議が正常に保存されたことを意味します。

呼び出し後に会議が正常に保存されたという答えの場合は、(コミットのために)管理meetingReposity.Save();する必要があるのはそれだけです。MeetingService

他のすべては、本質的に、会議が保存されたという事実の副作用です(現在、過去形で話していることに注意してください)。この時点で、他の各リポジトリのイベントサブスクリプションの方が理にかなっています。

これには、すべての条件のロジックを、そのロジックを処理するためにSRPに続くサブスクライバークラスに分離するという優れた効果もあります。これは、たとえば、連絡先リポジトリがコミットするときのロジックが変更された場合に重要になります。

お役に立てれば。

于 2012-06-16T23:13:12.677 に答える
1

最初の3つの回答はそれぞれ、問題を要約で処理するための重要な提案とアイデアを提供します。ただし、上記の例を読みすぎている可能性がありますが、これは、依存関係自体が多すぎるのではなく、集約ルートが多すぎるという問題のように見えます。これは、リポジトリインジェクションインフラストラクチャの基盤となる永続性メカニズムの欠如、またはその設定の誤りと関係があります。

簡単に言うと、連絡先、出席者、メモなどです。会議自体の複合プロパティである必要があります(個別に管理されている連絡先、&c。オブジェクト/データへのリンクとしてのみ)。したがって、永続化メカニズムはそれらを自動的に保存する必要があります。

「コンストラクターの乱用は単なる症状である」というブライアン・ワッツの格言に注意してください。他にもいくつかの可能性があります。

  • 永続化メカニズムは、会議グラフの永続性を自動的に処理する必要があり、構成が誤っているか、これを実行する機能がありません(Bryanが提案する3つすべてで、これを実行します。DbContext(EF 4.1+)を追加します)。この場合、依存関係は実際には1つだけである必要がありますIMeetingRepositoy---会議とそのコンポジット自体のアトミック保存を処理できます。
  • SaveMeeting()他のオブジェクト(連絡先、出席者など)へのリンクを保存するだけでなく、それらのオブジェクトも保存します。その場合、dtryonに同意する必要がMeetingServiceありSaveMeeting()、名前が示すよりもはるかに多くのことを実行します。彼のメカニズムはそれを軽減します。
于 2012-06-19T16:37:24.020 に答える
0

リポジトリ機能をその数のインターフェイスに分割する必要が本当にありますか?それらを別々にモックする必要がありますか?そうでない場合は、より多くのメソッドを使用して、インターフェイスを少なくすることができます。

しかし、あなたのクラスが本当に多くの依存関係を必要としていると仮定しましょう。その場合、次のことができます。

  • MeetingServiceBindingsすべての依存関係を提供する構成オブジェクト()を作成します。単一のサービスだけでなく、モジュール全体ごとに単一の構成オブジェクトを持つことができます。このソリューションに問題はないと思います。
  • NInjectなどの依存性注入ツールを使用します。非常に簡単です。依存関係をコードで1か所で構成でき、クレイジーなXMLファイルは必要ありません。
于 2012-06-16T22:20:05.847 に答える