依赖注入:它会让你的代码更难改变吗?
Posted
技术标签:
【中文标题】依赖注入:它会让你的代码更难改变吗?【英文标题】:Dependency Injection: could it make your code harder to change down the line? 【发布时间】:2022-01-18 15:21:23 【问题描述】:据我了解,基本的依赖注入意味着不是在类内部创建依赖项,而是在外部创建依赖项并将其作为参数传入。
假设您有一个 Logger 类,它执行一些操作,然后写入日志,它依赖于 WriteToFile 对象。无需在 Logger 类中创建 WriteToFile 对象,而是在外部创建它,并在每次创建新的 Logger 实例时将其作为参数传入。
让我感到困惑的是,假设您的代码中有 1,000 个地方创建了 Logger 对象,并假设出于某种原因您不再需要在 Logger 类中使用 WriteToFile 对象...
如果没有 DI,您只需删除 Logger 类中创建并使用 WriteToFile 对象的代码。这可能需要几秒钟。
但是使用 DI,您必须找到创建 Logger 对象的那 1,000 个位置,并删除创建 WriteToFile 对象的代码并将其作为参数传递。
这是正确的还是我遗漏了一些重要的东西?
【问题讨论】:
依赖注入在一个位置创建所有服务:Composition Root... 但Logging is not a Service,这是一个横切关注点。 【参考方案1】:应用 DI 并不一定意味着您将类完全由内而外,这样 所有 类的依赖项都是从外部提供的。这很容易导致无法维护的混乱。
这就是为什么从 DI 的角度来看,我们将依赖项分为 2 个不同的组:
稳定的依赖关系: 是行为具有确定性的类(和功能),您永远不会期望必须替换、包装、装饰或拦截。 Volatile Dependencies: 所有不稳定的依赖项,根据定义都是易变的。这些类(和功能)的行为要么是不确定的(例如Random.Next
、DateTime.Now
,或在调用数据库时),要么是您希望能够替换、包装、装饰或拦截的代码库的一部分.
实际上还有更多稳定和易变的依赖。可以在here 找到对这些概念的更详细描述。
从 DI 的角度来看,我们只对 Volatile Dependencies 感兴趣。这些是我们希望隐藏在抽象后面并通过构造函数注入的依赖项。抽象和注入 Volatile Dependencies 带来了许多有趣的优势,例如:
灵活性/可维护性。例如通过依赖ILogger
,您可以通过更改单行代码(或翻转配置开关)将日志写入数据库而不是文件。
可测试性:通过允许替换易失性依赖项,单独测试单个类变得更加容易。
另一方面,稳定依赖项不需要根据定义进行交换,与它们的紧密耦合不会妨碍可测试性,因为它们的行为是确定性的。
如果我们查看您的具体情况,您的记录器就是易失性依赖的一个很好的例子:
记录器通常是您希望在模型等插件中替换的类;明天你可能想要登录到数据库。 您的记录器写入磁盘;这是不确定的行为。 您可能希望在测试时将记录器替换为假实现。这意味着您系统中的大多数类不应依赖于您的记录器实现,而是依赖于日志记录抽象。
正如您所解释的,您的记录器类包含一个WriteToFile
对象。要了解是否应该使用 DI 从外部将其提供给记录器,意味着您需要确定 WriteToFile
是否 - 从记录器的角度来看 - 一个易失性依赖关系。很可能这个WriteToFile
对象是您的记录器类的固有部分。它们一起形成一个单一的组件。通常期望单个组件中的类是紧密耦合的。在这种情况下,从记录器的角度来看,WriteToFile
是一个稳定的依赖关系。
在您从 logger 的角度确定 WriteToFile
为稳定依赖后,无需将其隐藏在抽象后面并将其注入 Logger。这只会增加开销而不会增加任何好处。
将 DI 应用于稳定依赖“使您的代码更难更改”,而将 DI 应用于易失性依赖使您的代码更易于维护。
【讨论】:
很好的答案,谢谢!以上是关于依赖注入:它会让你的代码更难改变吗?的主要内容,如果未能解决你的问题,请参考以下文章