4

我正在寻找避免构造函数注入过度使用的最佳实践。例如,我有会议实体,它有几个子实体,如下所示:

  • 会议
    1. 会议联系人
    2. 会议与会者
    3. 会议类型
    4. 地址
    5. 会议公司
    6. 会议记录

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
}

这里只是七个依赖项,但实际代码包含更多。我使用了“依赖注入构造函数疯狂”中描述的不同技术,但我还没有找到如何处理存储库依赖关系。

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

4

4 回答 4

4

构造函数过度使用只是一个症状 - 似乎您通过拥有一个了解消息持久性的各种元素并将它们插入整体保存的“主”类来近似一个工作单元。

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

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

您可以在我以前的回答中找到有关存储库如何工作的更多信息:

为每个对象创建通用存储库与特定存储库的优势?

拥有存储库后,您将确定它们将在其中运行的上下文。这通常映射到单个 Web 请求:在请求开始时创建公共工作单元的实例并将其交给所有存储库。在请求结束时,保存工作单元中的更改,让存储库不必担心访问哪些数据。

这巧妙地将所有内容捕获并保存为一个单元。这与源代码控制系统的工作副本非常相似:将系统的当前状态拉入本地上下文,使用它,并在完成后保存更改。您不会单独保存每个文件 - 您将它们全部同时保存为离散修订。

于 2012-06-17T04:19:57.460 回答
3

稍微扩展一下我上面的评论:

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

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

本质上,其他所有内容都是会议已保存这一事实的副作用(请注意,现在我们用过去时态说话)。在这一点上,对其他每个存储库的事件订阅更有意义。

这还具有将所有条件中的逻辑分离到遵循 SRP 以处理该逻辑的订阅者类的良好效果。例如,当联系人存储库提交的逻辑发生更改时,这一点变得很重要。

希望这可以帮助。

于 2012-06-16T23:13:12.677 回答
1

前三个答案中的每一个都为处理摘要中的问题提供了重要的建议和想法。但是,我可能对您上面的示例阅读过多,但这看起来像是聚合根过多的问题,本身并没有太多的依赖关系。这与缺少存储库注入基础设施底层的持久性机制或配置错误有关。

简单地说,就是联系人、与会者、笔记等。应该是会议本身的复合属性(如果仅作为链接到单独管理的联系人等对象/数据);因此,您的持久性机制应该自动保存它们。

听从 Bryan Watts 的格言“构造函数过度使用只是一种症状”,还有其他几种可能性:

  • 您的持久性机制应该自动处理会议图的持久性,并且配置错误或缺乏执行此操作的能力(Bryan 建议执行此操作的所有三个,我将添加DbContext (EF 4.1+))。在这种情况下,实际上应该只有一个依赖IMeetingRepositoy项——它可以处理会议及其组合本身的原子保存。
  • SaveMeeting()不仅保存了指向其他对象(联系人、与会者等)的链接,而且还保存了这些对象,在这种情况下,我必须同意 dtryon 的观点,MeetingService并且SaveMeeting()所做的远远超出名称所暗示的范围,他的机制可以减轻它。
于 2012-06-19T16:37:24.020 回答
0

您真的需要将存储库功能拆分为这么多接口吗?你需要单独模拟它们吗?如果没有,您可以使用更少的接口,使用更多的方法。

但是让我们假设您的班级确实需要那么多依赖项。在这种情况下,您可以:

  • 创建一个MeetingServiceBindings提供所有依赖项的配置对象 ( )。每个模块可以有一个配置对象,而不仅仅是单个服务。我认为这个解决方案没有任何问题。
  • 使用依赖注入工具,例如NInject。这很简单,您可以在一个地方在代码中配置您的依赖项,并且不需要任何疯狂的 XML 文件。
于 2012-06-16T22:20:05.847 回答