Dependency injection 依赖倒置原则是否意味着我必须为每个模块创建一个接口?

Dependency injection 依赖倒置原则是否意味着我必须为每个模块创建一个接口?,dependency-injection,architecture,solid-principles,dependency-inversion,Dependency Injection,Architecture,Solid Principles,Dependency Inversion,如果我希望我的代码遵循坚实的原则,特别是依赖倒置原则,这是否意味着我必须为每个模块创建一个接口(抽象),即使它只有一个实现 在我看来,根据这些帖子: 为每个模块创建“抽象”是一种代码混乱,违反了YAGNI原则 我的经验法则是:不要使用依赖注入,或者为模块创建接口,除非它有多个实现(第二个实现可以是模拟类,用于数据库/服务器/文件模块的单元测试) 有人能帮我澄清一下吗?SOLID是否意味着我必须注入每个模块并对其进行抽象?如果是的话,难道这不是一堆杂乱的东西吗?我们大多数时候都不会使用这些东西

如果我希望我的代码遵循坚实的原则,特别是依赖倒置原则,这是否意味着我必须为每个模块创建一个接口(抽象),即使它只有一个实现

在我看来,根据这些帖子:

为每个模块创建“抽象”是一种代码混乱,违反了YAGNI原则

我的经验法则是:不要使用依赖注入,或者为模块创建接口,除非它有多个实现(第二个实现可以是模拟类,用于数据库/服务器/文件模块的单元测试)


有人能帮我澄清一下吗?SOLID是否意味着我必须注入每个模块并对其进行抽象?如果是的话,难道这不是一堆杂乱的东西吗?我们大多数时候都不会使用这些东西。

依赖倒置原则指出:

高级模块不应依赖于低级模块。两者都应该 依赖于抽象

换句话说,所依赖的每个模块(即应用程序中除了入口点模块之外的所有模块)都应该抽象。否则,高电平模块将不得不直接依赖低电平模块,从而导致违反DIP

抽象实现的数量与DIP无关,因为它的目标是使模块抵制更改。如果没有抽象,就不可能轻松地更改实现或添加横切关注点,而不必更改或重新编译高级组件

但是,如果您发现自己仅用一个实现定义了许多抽象,那么您就违反了,正如Mark Seemann在您引用的文章中所述:

给定接口只有一个实现是一种代码味道

不过,这并不是说您不应该定义接口,而是说您需要仔细查看您的设计并找出与行为相关的类。这些相关的类通常可以放在同一个通用抽象(通用接口)后面,这不仅允许抽象重用,而且使应用横切关注点成为儿戏

下面是一些关于功能的建议,您可以将这些建议放在相同的通用抽象背后:

  • 对于代表用户对系统进行更改的类(用例)
  • 作为查询数据库(或文件系统、web服务等)并返回数据的类的抽象
  • IValidator
    用于检查向用户报告验证错误的类
  • ISecurityValidator
    用于验证是否允许用户执行特定操作的类
  • IAuthorizationFilter
    用于允许基于用户权限和角色应用基于行的安全性的类
  • IEventHandler
    用于响应已发生的特定业务事件的类
这些只是一些抽象的例子。这在很大程度上取决于应用程序和设计,您将获得哪些通用抽象

我编写的应用程序使用这些通用抽象,这些应用程序只有几个接口,只有一个实现。系统中大约90%到98%的模块实现这些通用抽象之一(取决于应用程序的大小;应用程序越大,百分比越高)

这些通用抽象使得在DI库的一行代码中注册所有实现变得非常容易(或者至少,如果您使用的是.NET),但更重要的是,正如我前面所说的,应用横切关注点变得非常容易。例如,不必对应用程序进行彻底的更改,您可以在数据库事务中运行用例,或者应用死锁重试机制。或者,您可以应用查询缓存,而不必在整个应用程序中进行彻底的更改