【问题标题】:Preventing static injection when using builder pattern使用构建器模式时防止静态注入
【发布时间】:2016-01-22 21:04:48
【问题描述】:

简介

我将 Dagger 2 用于使用DDD 的原则建模的项目。

我想使用Builder Pattern 创建复杂的实体,而我通常获取构建器实例的方式是使用静态工厂方法:ComplexEntity.builder()。

聚合根将获得强类型 ID,即 ComplexEntityID,这需要新实例的值生成器。现在我的模块中有一个方法:@Provides ComplexEntityID provideComplexEntityID(IdValueGenerator generator) { return new ComplexEntityID(generator.nextId(); }。

ComplexEntityBuilder 需要ComplexEntityID 的实例(或提供者)才能创建ComplexEntity。但是 Guice 和 Dagger(以及其他)的人建议不要使用静态注入,这是有充分理由的。

问题

如何使用构建器创建ComplexEntity 的实例,而不使用静态注入?也就是说,如何获取builder的实例?

坚持ComplexEntity.builder() 会很好,因为这是一个通用约定,但我认为在不使用new () 关键字的情况下使实例在静态方法中可用的唯一方法是使用静态注入。

我能想到的另一种方法是创建ComplexEntityFactory 并将builder() 方法放在那里。但是同时使用 factory 和 builder 似乎有点奇怪:

  1. 在需要的类中注入工厂
  2. complexEntityFactory.builder().value1(...) .value2(...) .build();

在这种情况下推荐的方法是什么?

编辑:如果它毕竟是一个专用工厂,如果这个工厂仍然可以使用AutoFactory 或类似的东西生成,那就太好了

【问题讨论】:

  • 您可以将ComplexEntityID 传递给静态工厂方法:ComplexEntity.builder(complexEntityID)。构建器的构造函数采用所需参数并不罕见。
  • 我想这是一个合理的解决方案。这确实意味着无论我需要在哪里构建ComplexEntity,我都会依赖Provider<ComplexEntityID>。
  • @jaco0646 作为 API 的使用者,您是否会发现需要 Provider<ComplexEntityID> 实例的 ComplexEntity.builder(ComplexEntityID id) 比获取 ComplexEntityFactory 的实例更直观/可用,并从那里获取构建器(考虑到任何一种方法都会在整个应用程序中一致使用)?
  • 这取决于消费者是否知道该 ID。我不会仅出于此目的将 ID 引入 API;但如果消费者在其他地方使用该 ID,那么再次使用它会增加很少的概念开销。
  • 感谢您的洞察@jaco0646。消费者会知道该 ID,但仅用于参考而不用于实例化(为新实体动态创建,或为现有实体反序列化)。我尝试了一段时间,并意识到更改名称会有所作为;我最终得到了一个BuilderFactory,其中包含特定有界上下文的所有相关构建器。它主要用于命令,因此这在 API 中提供了一个很好的中心位置,可以从中为该上下文构建命令。例如。 workspaceCommandBuilderFactory.archiveWorkspace()...build();

标签: java design-patterns dependency-injection domain-driven-design


【解决方案1】:

我认为有一个比jaco0646在评论中提出的更优雅的解决方案:

构建器是一种特殊的服务:您使用它们来实现特定目标,但它们在领域内没有生命周期或意义。

因此,只需像注入任何其他服务一样注入构建器。

这有几个优点:

  • 构建者可以通过依赖其他服务和工厂来使用它们。在您的情况下,这是对 ComplexEntityID 工厂的依赖。
  • 对构建器的依赖是明确的(这是一件好事)。目前,您的依赖项隐藏在对静态方法的调用中。
  • 您可以在测试中替换构建器。

总体而言,这种方法提高了内聚性并减少了耦合。

需要注意的一个潜在缺陷是构建器通常具有内部状态(与典型服务不同)。因此,请确保配置您的依赖注入容器,以便为每个解析请求创建一个新的构建器实例。

【讨论】:

  • 感谢@theDmi,我认为这确实是最优雅的解决方案。我最终做的是注入一个构建器,其中包含聚合的特定类型的所有构建器。因此对于Workspace 聚合,所有命令都可以通过注入WorkspaceCommandBuilder 来构建。对于每个返回该命令的构建器的命令,它都有一个方法,使用如下:workspaceCommandBuilder.archiveWorkspace()...build() 或workspaceCommandBuilder.addWorkspace()....build()。看WorkspaceCommandBuilder类可能不是真正的builder,但在实际的api使用中是一样的
猜你喜欢
  • 2019-11-16
  • 2016-02-22
  • 2014-02-10
  • 2015-04-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-21
相关资源
最近更新 更多