代码之家  ›  专栏  ›  技术社区  ›  Nikita Fedyashev

混合基于构造函数和基于setter的注入是一件坏事吗?

  •  13
  • Nikita Fedyashev  · 技术社区  · 16 年前

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

    所有这些参数的使用寿命都相同。最后我们必须有一个 Immutable Object .

    由于它对可读性的影响,我不敢将它们全部列在构造函数中,于是决定将其中3个移到setters注入中。但显然这不是一个优雅的解决方案。

    问题:

    1)混合基于构造函数和基于setter的注入是否是一种坏做法?

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

    我在考虑应用MartinFowler的“引入参数对象”重构,但这有一个问题。

    4个参数可以很容易地移动到参数对象(customerid、projectid、languageid等)——所有整数。

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

    1 回复  |  直到 8 年前
        1
  •  25
  •   Andrew Swan    8 年前

    将构造函数注入和属性注入混合在一起并不一定是件坏事,但这可能并不常见。作为一个整体策略,避免资产注入,因为它更难正确地实现(这听起来可能有悖直觉,但这是真的)。

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

    • 构造函数注入应该是您的默认注入模式。它非常容易实现并且可以保证不变量:将其分配到只读字段以确保使用者的不变量。
    • 当你有一个良好的 本地默认值 实现,但您希望遵循 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。