【问题标题】:IoC and singletons - when not to injectIoC 和单例 - 何时不注入
【发布时间】:2013-01-27 09:53:00
【问题描述】:

阅读了一些关于领域驱动设计的有趣书籍,示例如下:

// Order
IOrderNumberService orderNumberService;
public void Accept()
{
    this.orderNumber = this.orderNumberService.GetNextAvailableNumber();
}

上面代码中没有显示的是OrderNumberService已经被一个IoC框架注入了。我们不知道服务对象的生命周期范围(可以为每次注入创建一个新服务,或者每次注入一个单例)。

对于一个工作是从数据库中检查下一个可用数字的服务,我认为不需要状态并且可以安全地(可能应该)使用单例。

对于这种情况,我认为不需要换出这个服务,并且可以直接调用单例类的具体实现OrderNumberService.GetNextAvailableNumber(),或者至少如果我需要使用一个接口进行抽象,我总是可以重构在那个时候晚些时候。

除了从实现中抽象出服务接口以便于测试之外,注入服务还有其他好处吗?

我很谨慎地将我读过的所有内容都放入代码中,因为它们很酷 - 那么抽象在什么时候创建了如此多的层,这使得代码更难阅读,应该注意哪些警告信号?

【问题讨论】:

  • 虽然对于实体/AR方法中使用的服务(和其他对象)有不同的看法,但我认为实体不应该对外界非常熟悉:) --- 也许@在这种情况下,987654324@ 或 public void Accept(string orderNumber) 就足够了。

标签: architecture domain-driven-design inversion-of-control


【解决方案1】:

服务在 DDD 中没有状态 (Evans p. 107),无论它们属于哪个层

当领域中的重要过程或转换不是 ENTITY 或 VALUE OBJECT 的自然责任,添加操作 到模型作为声明为服务的独立接口。定义 模型语言方面的界面,并确保 操作名称是 UBIQUITOUS LANGUAGE 的一部分。 做服务 无国籍

使用高级抽象(接口)而不是实现(类)是Dependency Inversion principle,并且不是依赖注入,依赖倒置是关于抽象和实现的关系,而依赖注入是关于删除硬编码的依赖 - 甚至如果您的字段是一个接口,您仍然需要以某种方式对其进行初始化,即为某个类实例分配一个值。

至于注入服务实例的技术,DDD 与技术无关。如果您认为使用 IoC 容器会使事情变得更难阅读,您可以考虑改用服务定位器模式,完整的推理请参见 Martin J Fowler article on DI containers。但是 tbh DDD 需要一些复杂的团队(Evans 在一些关于 infoq 的谈话中提到过)所以我认为将是能够掌握 IoC 概念的人,所以服务定位器在这里没有太大的优势(除非你正在工作是一些有限的环境,例如 IoC 框架过于重量级的 Android 手机解决方案)

至于臭名昭著的单例模式(用静态类方法实现)我认为它根本不合适,因为它不可测试。

DDD 还提倡使用 轻量级 框架(参见 Architectural Frameworks, Evans p. 74),而当代框架(Spring、Fluent NHibernate 等)的开发考虑到了这一点,因此它们实际上是旨在使 POJO/POCO 的工作更简单(您可以在一组关于框架设计的文章中阅读更多内容,由 Fluent NHibernate 架构师 Jeremy Miller http://codebetter.com/jeremymiller/2010/01/11/patterns-in-practice-a-retrospective/ 撰写),许多 Spring 项目,例如直接参考 DDD。因此,框架不太可能强迫您在代码中添加依赖项,或者降低其可读性 - 它宁愿使用反射 API 来设置值,并且会使用一些非侵入性的方法,如 XML 或属性(注释)配置。

【讨论】:

    【解决方案2】:

    除了从实现中抽象出服务接口 允许更轻松的测试,注入 服务?

    依赖注入通过易于管理的配置将元素连接在一起。正如您所指出的,一个用例是使用模拟服务测试组件。总的来说,未来可能会出现这种服务实现的许多不同变体,通过配置来切换它们会更容易,而不是在很多地方重构代码。

    虽然OrderNumberService.GetNextAvailableNumber() 最初可能看起来是微不足道的实现,可能不会改变,但从理论上讲,未来需求仍有可能发生变化。

    想象一下这种情况:您的客户使用您的系统获得了一些其他业务,并希望您在那里安装另一个系统实例。但是他们的会计部门在对订单进行编号时使用了一些基于日期的模式,而不是简单地增加 ID。通过使用 DI,您可以让两个实例运行不同的配置,而无需重构代码。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-06-02
      • 1970-01-01
      • 2011-12-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多