【问题标题】:Is mixing constructor-based and setter-based injections a bad thing?混合基于构造函数和基于 setter 的注入是一件坏事吗?
【发布时间】:2009-12-31 14:57:14
【问题描述】:

我有一个从 CSV 文件操作导入产品的类,它需要大约 7 个参数。这是进口商绝对需要的信息。

所有这些参数都有相同的生命周期。最后我们必须有一个Immutable Object

我太害怕在构造函数中列出所有这些,因为它会影响可读性,并决定将其中的 3 个移到 setter 注入中。但显然这不是一个优雅的解决方案。

问题:

1) 混合使用基于构造函数和基于 setter 的注入是一种不好的做法吗?

2) 如何解决这个特定问题?

我在考虑应用 Martin Fowler 的“Introduce Parameter Object”重构,但这样做有问题。

4 参数可以很容易地移动到 Parameter 对象(customerId、projectId、languageId 等) - 所有整数。

其他 3 个参数是我注入的对象(模拟单元测试需要它)。

【问题讨论】:

  • 这将取决于您的 DI 容器...有些比其他容器更容易。
  • @skaffmann:我强烈反对。 DI 模式的使用不应该由 DI 容器的选择来决定 - 容器可以帮助您,而不是限制您。
  • @Nikita - 为什么不引入完整的参数对象并将你的模拟注入参数?

标签: oop refactoring dependency-injection setter


【解决方案1】:

将构造函数注入和属性注入混合起来不一定是坏事,但它可能并不常见。作为一个整体策略,请避免属性注入,因为它更难以正确实施(这听起来可能违反直觉,但确实如此)。

了解何时使用每种模式很重要。

  • 构造函数注入应该是您的默认注入模式。它非常容易实现,并且可以保证不变量:将其分配给只读字段以确保消费者的不变量。
  • 当您拥有良好的本地默认实现,但您希望遵循Open/Closed Principle 并允许高级用户通过提供替代实现来扩展类时,可以使用属性注入。

你不应该因为构造函数化妆品而应用属性注入。

当您需要太多依赖项时,这表明您可能违反了Single Responsibility Principle - 该类只是试图一次做太多事情。

与其引入参数对象(否则是一个好建议),更好的选择是将两个或多个依赖项封装到一个聚合服务中,以协调这些依赖项的交互。

想象一下您的初始构造函数如下所示:

public MyClass(IDep1 dep1, IDep2 dep2, IDep3 dep3, IDep4 dep4, IDep5 dep5)

经过一些分析,您会发现在这种情况下 IDep1、IDep3 和 IDep4 将以特定方式一起使用。这将允许您引入一个像这样封装这些的聚合服务:

public class AggService : IAggService
{
    public AggService(IDep1 dep1, IDep3 dep3, IDep4 dep4)
    {
        // ...
    }

    // ...
}

您现在可以将原来的构造函数改写成这样:

public MyClass(IAggService aggSrvc, IDep2 dep2, IDep5 dep5)

等等……

很常见的是,聚合服务本身就是一个合适的概念,突然之间,您拥有比刚开始时更丰富的 API。

【讨论】:

  • 聚合服务与参数对象有何不同?听起来您是在暗示它可能与域模型具有更具表现力、更具体的关系,但对于精心设计的参数对象来说应该是这样,不是吗?
  • @Jeff Sternal:聚合服务可能与参数对象没有太大区别,但聚合服务更倾向于好莱坞原则(一件好事)。参数对象通常只是保存相关对象的结构,您可以单独提取和使用这些对象。聚合服务将对消费者隐藏这些依赖关系,而是提供整体功能。不过,这绝不是一个明确的区别……
  • @Mark:我能在您的《.NET 中的依赖注入》一书中找到更多关于参数对象和聚合服务之间区别的信息吗?
猜你喜欢
  • 1970-01-01
  • 2021-08-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-05
  • 1970-01-01
  • 1970-01-01
  • 2017-12-15
相关资源
最近更新 更多