阅读一些关于域驱动设计的有趣书籍,一个样本如下:
// Order
IOrderNumberService orderNumberService;
public void Accept()
{
this.orderNumber = this.orderNumberService.GetNextAvailableNumber();
}
上述代码中未显示的是{I}框架已注入OrderNumberService
。我们不知道服务对象的生命周期范围(可以为每次注入创建一个新服务,或者每次注入一个单例)。
对于一个服务,它的工作是检查数据库中的下一个可用数字,我看不到需要的状态,可以安全地(可能应该)使用Singleton。
对于这种情况,我没有看到交换这个服务的需要,并且可以直接调用单例类具体实现OrderNumberService.GetNextAvailableNumber()
,或者至少我需要使用接口进行抽象我当时可以随时重构。
除了从实现中抽象服务接口以便于测试之外,注入服务还有其他好处吗?
我很谨慎将我读过的所有内容都放在代码中只是因为它们很酷 - 所以抽象创建了如此多的层次会使代码更难以阅读以及应该注意的警告标志是什么?
答案 0 :(得分:3)
服务在DDD中没有状态(Evans第107页),无论他们属于哪一层
当域中的重要流程或转换不是a ENTITY或VALUE OBJECT的自然责任,添加操作 将模型作为声明为SERVICE的独立接口。限定 关于模型的语言界面,并确保 操作名称是UBIQUITOUS LANGUAGE的一部分。 提供服务 无状态强>
使用高级抽象(接口)而不是实现(类)是Dependency Inversion principle,并且不是依赖注入,依赖性反转是关于抽象和实现的关系,而依赖注入是关于删除硬编码的依赖关系 - 即使你的字段是一个接口,你仍然需要以某种方式初始化它,即为某个类实例赋值。
对于注入服务实例的技术,DDD与技术无关。如果您认为使用IoC容器会使事情变得更难阅读,您可以考虑使用Service Locator模式,完整的推理请参阅Martin J Fowler article on DI containers。但是DDD需要一些成熟的团队(Evans在一些关于infoq的讨论中提到它)所以我认为将会有人能够掌握IoC概念,所以Service Locator在这里没有什么大的优势(除非你工作的是一些有限的环境,如Android手机,其中IoC框架是太重量级的解决方案)
至于臭名昭着的单一模式(用静态类方法实现)我认为它根本不合适,因为它不可测试。
此外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或属性(注释)配置。
答案 1 :(得分:2)
除了从实现中抽象服务接口之外 允许更容易测试,注入的任何其他好处 服务?
依赖注入通过易于管理的配置将元素连接在一起。正如您所指出的,一个用例是使用模拟服务测试组件。一般来说,这种服务实现的许多不同变体可能会在未来出现,通过配置切换它们会比在很多地方重构代码更容易。
虽然OrderNumberService.GetNextAvailableNumber()
最初似乎可能是微不足道的实现,可能不会改变,但理论上可能在将来需求发生变化。
想象一下这种情况:您的客户使用您的系统获得了一些其他业务,并希望您在那里安装另一个系统实例。但他们的会计部门在编号时使用了一些基于日期的模式而不是简单地增加ID。通过使用DI,您可以有两个运行不同配置的实例,而无需重构代码。