在进行构造函数或设置器注入时,依赖项的正确粒度是多少?

Posted

技术标签:

【中文标题】在进行构造函数或设置器注入时,依赖项的正确粒度是多少?【英文标题】:What is the right granularity for dependencies while doing constructor or setter injection? 【发布时间】:2013-03-07 03:33:43 【问题描述】:

我正在尝试为自己定义一些依赖注入指南。在为要通过构造函数或 setter 注入注入的类定义依赖项时,正确的粒度应该是多少?该类可以是服务、存储库等。假设有一个存储库类,如下所示:

public class ProductRepository 

    //Option-A
    public ProductRepository(DataSource dataSource)
    
    

    //Option-B      
    public ProductRepository(SqlSession sqlSession)
    
    

    //Option-C
    public ProductRepository(SqlSessionTemplate sqlSessionTemplate)
    
    

上述类所需的最小依赖是DataSource接口。存储库类在内部使用 SqlSessionTemplate(SqlSession 接口的实现)。如代码所示,构造函数注入有 3 种选择。以下是我的理解:

Option-A(数据源依赖) 这是存储库类的最小依赖项。从消费者的角度来看,此构造函数是正确的选择,但从单元测试的角度来看,它不适合,因为 DataSource 在存储库实现中由 SqlSessionTemplate 在内部使用。

Options-B(SqlSession 依赖) 从单元测试的角度来看,这是正确的选择,但不是从消费者的角度来看。此外,存储库实现与 SqlSessionTemplate 接口的特定实现紧密耦合。因此,如果消费者传递了除 SqlSessionTemplate 之外的其他 SqlSession 接口,它将无法工作。

Options-C(SqlSessionTemplate 依赖) SqlSessionTemplate 作为实现而不是接口似乎不适合单元测试。此外,与 DataSource 相比,实例化 SqlSessionTemplate 对消费者不利。因此放弃了这个选项。

选项-A 和选项-B 似乎是可用的选择。但是,消费者的观点和单元测试的观点之间需要权衡取舍,反之亦然。

我是依赖注入的新手。我向 DI 专家寻求建议。什么是正确的解决方案(如果有)?在上述情况下你会怎么做?

谢谢。

【问题讨论】:

【参考方案1】:

这是存储库类的最小依赖项。

我认为这是确定合适的耦合量的起点。您应该注入不超过或少于满足要求所需的量。

这是一个非常笼统的指导方针,几乎相当于“视情况而定”,但这是开始思考它的好方法。我对 DataSource、SqlSession 或 SqlSessionTemplate 了解不够,无法在上下文中回答。

存储库类在内部使用 SqlSessionTemplate (SqlSession接口的实现)

为什么存储库不能简单地将接口用作依赖项?接口不覆盖实现的所有公共方法吗?如果不是,接口是否是一个有用的抽象?

我无法完全拼凑出你想要做什么以及依赖项是如何工作的,但我最好的猜测是:

    您需要同时通过构造函数注入 SqlSession 和 DataSource,或者 需要通过Repository的构造函数注入SqlSession,将DataSource注入SqlSessionTemplate的构造函数

【讨论】:

我故意没有在问题中附加 Java 标记。这个问题是概念性的,它同样适用于.net(如果你属于它)。困境在于 API 的可用性和单元测试之间的权衡。我以存储库为例,但它可能是一个有很多消费者的框架类。为了提供更多上下文,SqlSessionTemplate 是 SqlSession 的实现,但它不完全遵守接口契约。换句话说,IMO,它违反了 LSP(Liskov Subst. Principle)。 SqlSession 接口可以认为是轻量级的 ORM 映射器。 概念上DataSource类似于ADO.net SqlConnection。【参考方案2】:

您正在谈论对存储库进行单元测试,但这通常是无用的,因为存储库是您通往数据库的门户,并且与它有很强的耦合性。单元测试应单独进行,但存储库只能使用数据库进行测试。因此:集成测试。

如果您能够从存储库中抽象出特定于数据库的逻辑(正如您似乎正在做的那样),那么就没有什么需要测试的了,因为存储库的职责是与数据库进行通信。如果还有很多东西需要测试,那么……在这种情况下,您的存储库类可能违反了Single Responsibility Principle,这使得您的存储库难以维护。

因此,由于您通常会使用数据库测试存储库本身,因此从测试的角度来看,您注入什么并不重要,因为您必须以能够连接到无论如何都是数据库。

【讨论】:

我知道集成测试不仅仅适用于存储库的单元测试。我以存储库为例。它可以是一个服务类,也可以是一个低级框架类。您可以使用非存储库类来可视化这种情况。

以上是关于在进行构造函数或设置器注入时,依赖项的正确粒度是多少?的主要内容,如果未能解决你的问题,请参考以下文章

Angular 2 中的依赖注入在使用内联注入器和构造器注入时创建多个实例

Ninject之旅之七:Ninject依赖注入

依赖注入和服务定位器模式有啥区别?

构造函数和 ngOnInit 的区别

构造函数和 ngOnInit 的区别

控制反转定义[重复]