我一直在尝试在我的新项目中遵循域驱动的设计方法。我一直使用Spring进行依赖注入,这很好地将我的应用程序代码与构造代码分开,然而,使用DDD我似乎总是有一个域对象想要创建另一个域对象,这两个域对象都具有状态和行为。
例如,给定媒体文件,我们希望将其编码为不同的格式 - 媒体资产调用转码服务并接收回调:
class MediaAsset implements TranscodingResultListener {
private NetworkLocation permanentStorage;
private Transcoder transcoder;
public void transcodeTo(Format format){
transcoder.transcode(this,format);
}
public void onSuccessfulTranscode(TranscodeResult result){
Rendition rendition = new Rendition(this, result.getPath(), result.getFormat());
rendition.moveTo(permanentStorage);
}
}
这引发了两个问题:
有一个工厂来创建这个类是我考虑过的,但是包含导致问题的“new”关键字需要大量的代码开销。
这里有一种我想念的方法,还是我只是做错了?
答案 0 :(得分:3)
我认为在这种情况下注入RenditionFactory是正确的方法。我知道它需要额外的工作,但你也从你的班级中删除了SRP违规。在业务逻辑中构建对象通常很诱人,但我的经验是,对象或对象的注入在100次中有99次得到回报。特别是如果所提到的对象是复杂的,和/或如果它与系统资源交互。
答案 1 :(得分:2)
我认为你的单元测试方法是单独测试MediaAsset
。这样做,我认为工厂是常见的解决方案。
另一种方法是测试整个系统(或几乎整个系统)。让您的测试访问外部接口[1](用户界面,Web服务接口等),并为系统访问的所有外部系统(数据库,文件系统,外部服务等)创建测试双精度。然后让测试注入这些外部依赖项。
这样做,您可以让测试完全与行为有关。测试与实现细节分离。例如,您可以使用Rendition
的依赖注入,而不是:测试不关心。此外,您可能会发现MediaAsset
和Rendition
不是正确的概念[2],您可能需要将MediaAsset
拆分为两个,并将其中的一半与Rendition
合并。同样,你可以做到这一点而不用担心测试。
(免责声明:在外层测试并不总是有效。有时你需要测试常见的概念,这需要你编写微测试。然后你可能会再遇到这个问题。)
[1]最佳级别实际上可能是“域界面”,低于用户界面的级别,您可以使用域语言而不是字符串和整数,以及您可以在哪里谈论域操作而不是按钮点击和焦点事件
[2]也许这实际上是您的问题:MediaAsset
和Rendition
是正确的概念吗?如果你问你的域专家,他知道这些是什么吗?如果没有,你真的在做DDD吗?