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 
} 

这里有短短七年的依赖,但真正的代码包含更多的人。我使用了"Dependency Injection Constructor Madness"中描述的不同技术,但我还没有找到如何处理存储库依赖关系。

有什么办法可以减少依赖关系的数量并保持代码的可测性?

+2

创建一个MeetingConfiguration类,其中构造函数为您“放牧猫”。然后,您可以将MeetingConfiguration类传递给您正在初始化的任何内容。你不会解决多重过载的问题,但至少所有的重载都在一个地方。 – 2012-06-16 21:51:12

+7

如果你真的在这个'MeetingService'中写信给所有的存储库,它肯定会做很多事情。把'MeetingService'分成只有共享密钥的其他服务怎么办?我的意思是,你的工作单位是什么?如果会议在'meetingReposity.Save();'后完成,然后发起一个事件并让所有其他人订阅。 –

+1

我认为@ dtryon的建议要好得多。我不喜欢创建“配置”类的想法,只是为了隐藏你实际上有很多依赖关系。这只会让代码不那么清晰,但没有更好的组织。 –

回答

4

构造函数过度只是一个症状 - 看起来你近似于unit of work有一个“主”类,它知道消息持久性的各种元素并将它们插入到整体保存中。

缺点是每个存储库通过暴露专用的Save方法来传递其他人的独立性;但这是不正确的,因为SaveMeeting明确指出存储库不是独立的。

我建议识别或创建一个类型,存储库消耗;这集中了您的更改并允许您从一个地方保存它们。示例包括DataContext (LINQ to SQL)ISession (NHibernate)ObjectContext (Entity Framework)

您可以找到如何在信息库中可能我的一个以前的答案工作的详细信息:

Advantage of creating a generic repository vs. specific repository for each object?

一旦你的仓库,你会指明他们将采取行动的背景。这通常映射到单个Web请求:在请求开始时创建一个通用工作单元实例,并将其交给所有存储库。在请求结束时,将更改保存在工作单元中,使存储库免费,以便担心访问哪些数据的

这整齐地捕捉和保存一切作为一个单位。这与源代码管理系统的工作副本非常相似:将系统的当前状态拉入本地环境,使用它,并在完成后保存更改。您不会独立保存每个文件 - 您将它们全部保存为离散版本。

+0

感谢您的帮助。我发现你的答案非常有用。至于我的设计,它是这样构建的,因为我试图从ActiverRecord移动到Repository。所以实施工作单位将是我的下一步。 – k0stya

0

您是否真的需要将存储库功能拆分为多个接口?你需要分别嘲笑他们吗?如果不是,则可以使用更少的接口和更多的方法。

但让我们假设你的类真的需要那么多的依赖。在这种情况下,你可以:

  • 创建一个配置对象(MeetingServiceBindings),提供所有的依赖关系。每个模块可以有一个配置对象,而不仅仅是单个服务。我不认为这个解决方案有什么问题。
  • 使用依赖注入工具,如NInject。这很简单,你可以在一个地方配置代码的依赖关系,不需要任何疯狂的XML文件。
+0

OP已*使用依赖注入。这些辅助类的问题是,你最终重复使用它们,正如你所建议的那样!这听起来不错,除非你最终在没有辅助类暗示的所有依赖的类中重用它们。这使得你的真正的依赖非常模糊。 –

+0

关于模糊依赖关系的好处。然后,您应该为每个类创建一个配置对象,而不是整个模块。请注意,“DI工具”与“DI”不同。 –

+1

另一个解决方案就是保持代码原样,如果类真的有很多依赖和构造函数注入是首选。 –

3

为了扩大对我的上述评论一点点:

由于这个问题是针对如何管理存储库的依赖,我必须假设MeetingService是管理某种持久的承诺。在过去,当我看到类似MeetingService这样的课程有很多依赖性时,显然他们做得太多了。所以,你必须问自己,“我的交易边界是什么”。换句话说,您可以做出的最小承诺是什么,这意味着会议已成功保存。

如果答案是在呼叫meetingReposity.Save();后成功保存会议,那么这就是所有MeetingService应该管理的(用于提交)。

从本质上来说,其他一切都是保存会议这一事实的副作用(注意现在我们用过去式说话了)。此时,为每个其他存储库订阅事件更有意义。

这也具有将所有条件中的逻辑分离为遵循SRP来处理该逻辑的订户类的良好效果。例如,当联系人存储库提交的逻辑经历更改时,这变得很重要。

希望这会有所帮助。

1

前三个答案中的每一个都给出了处理抽象问题的重要建议和想法。但是,我可能会在上面的示例中看到太多内容,但这看起来像是一个聚合根过多的问题,而不是太多依赖本身。这与存储库注入基础架构中的持久性机制缺乏或配置错误有关。

简单地说,联系人,参加者,备注,& c。应该是会议本身的复合属性(如果仅作为单独管理联系人的链接,则为& c。objects/data);因此,您的持久性机制应该自动保存它们。

听取布赖恩·沃茨的格言,‘构造的滥用只是一种症状,’一对夫妇的其他可能性:

  • 你的持久性机制应处理自动会议图形的持久性,并且无论是配置错误或缺少有能力做到这一点(布赖恩提出的所有这三点,我会加DbContext (EF 4.1+))。在这种情况下,应该只有一个依赖项 - IMeetingRepositoy - 它可以处理会议及其组合本身的原子保存。
  • SaveMeeting()是节约不只是链接到其他对象(联系人,与会者,&角),但也节省了这些对象,以及,在这种情况下,我会用dtryon是MeetingServiceSaveMeeting()正在做的远远超过了同意名字暗示,他的机制可以缓解它。