3

我一直在尝试在我的新项目中遵循领域驱动的设计方法。我一直通常使用 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);
    }

}

这引发了两个问题:

  • 如果再现需要一些依赖项(例如 MediaAsset 需要“转码器”)并且我想使用 Spring 之类的东西来注入它们,那么我必须使用 AOP 才能让我的程序运行,这是我不喜欢的。
  • 如果我想要一个 MediaAsset 的单元测试来测试新格式是否移动到临时存储,那么我该怎么做?我无法模拟再现类来验证它是否调用了它的方法......将创建真正的再现类。

有一个工厂来创建这个类是我考虑过的,但是仅仅包含导致问题的“new”关键字就需要很多代码开销。

这里有没有我错过的方法,或者我只是做错了?

4

2 回答 2

3

我认为在这种情况下注入 RenditionFactory 是正确的方法。我知道这需要额外的工作,但你也从你的班级中删除了违反 SRP 的内容。在业务逻辑中构造对象通常很诱人,但我的经验是注入对象或对象工厂在 100 次中会获得 99 次的回报。特别是如果提到的对象很复杂,和/或如果它与系统资源交互。

于 2012-11-27T13:01:07.667 回答
2

我假设您的单元测试方法是单独测试MediaAsset。这样做,我认为工厂是常见的解决方案。

另一种方法是测试整个系统(或几乎整个系统)。让您的测试访问外部接口[1](用户界面、Web 服务接口等),并为系统访问的所有外部系统(数据库、文件系统、外部服务等)创建测试替身。然后让测试注入这些外部依赖。

这样做,您可以让测试全部与行为有关。测试与实现细节分离。例如,您可以对Rendition或不使用依赖注入:测试不在乎。此外,您可能会发现MediaAssetandRendition不是正确的概念[2],您可能需要将其分成MediaAsset两部分并将其中的一半与Rendition. 同样,您可以做到这一点而不必担心测试。

(免责声明:在外层进行测试并不总是有效。有时您需要测试常见的概念,这需要您编写微测试。然后您可能会再次遇到这个问题。)

[1] 最好的级别实际上可能是“域界面”,在用户界面下方的级别,您可以在其中使用域语言而不是字符串和整数,并且可以在其中讨论域操作而不是按钮单击和焦点事件。

[2] 也许这实际上是您的问题:是MediaAssetRendition正确的概念吗?如果你问你的领域专家,他知道这些是什么吗?如果没有,你真的在​​做 DDD 吗?

于 2012-11-27T11:56:38.197 回答