依赖关系反转原则是否意味着我必须为每个模块创建一个接口? [英] Does dependency inversion principle mean that I have to create an interface for every module?

查看:119
本文介绍了依赖关系反转原则是否意味着我必须为每个模块创建一个接口?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果我希望我的代码遵循SOLID原则,尤其是依赖关系反转原则,是否意味着即使每个模块只有一个实现,我也必须为每个模块创建一个接口(抽象)?

If I want my code to follow SOLID principles, specifically the dependency inversion principle, does that mean that I have to create an interface (abstraction) for every module, even if it has only one implementation ?

我认为并根据这些帖子:

In my opinion, and according to these posts:

http://josdejong.com/blog/2015/01/06/code-reuse/

http://blog.ploeh.dk/2010/12/02/Interfacesarenotabstractions/

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

creating an "abstraction" for every module is a code clutter and violates the YAGNI principle.

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

My rule of thumb is: do not use dependency injection, or create an interface for a module unless it has more than one implementation (the second implementation could be a mock class for unit testing in case of database/server/file modules).

有人可以帮我解决这个问题吗? SOLID是否意味着我必须注入每个模块并将其抽象化?如果是的话,难道不是很多时候我们就不会经常使用它吗?

Can somebody clear this up for me ? Does SOLID mean that I have to inject every module and abstract it ? If yes, isn't it just a lot of clutter we're simply not going to use most of the times ?

推荐答案

依赖倒置原则指出:

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

High-level modules should not depend on low-level modules. Both should depend on abstractions.

换句话说,应该抽象每个依赖的模块(因此,除了应用程序中的入口点模块之外,就是所有模块).否则,高级模块将不得不直接依赖于低级模块,从而导致违反DIP.

In other words, each module that is depended upon (so that's everything except the entry point modules in your application) should be abstracted. Otherwise a high-level module will have to depend upon the low-level module directly, causing a violation of the DIP.

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

The number of implemenations an abstraction has is irrelevant to the DIP, because its goal is to make modules resistent to change. Without abstractions, it would become impossible to easily change implementations or add cross-cutting concerns without having to change or recompile a high-level component.

但是,如果您发现自己仅用一种实现定义了许多抽象,则违反了

If, however, you find yourself defining many abstractions with just one implementation, you are violating the Reused Abstraction Principle, as Mark Seemann already stated in the article you are referencing:

给定接口只有一种实现是代码异味.

Having only one implementation of a given interface is a code smell.

但是,这意味着您根本不应该定义接口,而是需要仔细了解设计并发现与行为相关的类.这些相关的类通常可以放置在相同的通用抽象(通用接口)之后,这不仅允许抽象重用,而且还可以应用横切关注点,让孩子玩耍.

What this means, though, is not that you shouldn't define interfaces at all, but you need to take a good look at your design and spot the classes that are related in behavior. Those related classes can often be placed behind the same generic abstraction (generic interface) and this not only allows abstraction reuse, but makes applying cross-cutting concerns childs play.

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

Here are a few suggestions of functionality that you can place behind the same generic abstraction:

  • ICommandHandler< TCommand> 用于以下类代表用户对系统进行更改(用例).
  • IQueryHandler< TQuery,TResult> 作为查询数据库(或文件系统,Web服务,无论是什么)并返回数据的类的抽象.
  • IValidator<T>用于将报告验证错误返回给用户的类
  • ISecurityValidator<T>用于验证是否允许用户执行特定操作的类.
  • IAuthorizationFilter<T>用于允许基于用户的权限和角色应用基于行的安全性的类.
  • IEventHandler<T>用于响应已发生的某个业务事件的类.
  • ICommandHandler<TCommand> for classes that make changes to the system on behalf of the user (use cases).
  • IQueryHandler<TQuery, TResult> as an abstraction for classes that query the database (or file system, web service, what ever) and return data.
  • IValidator<T> for classes that check report validation errors back to the user
  • ISecurityValidator<T> for classes that verify whether a user is allowed to execute a certain operation.
  • IAuthorizationFilter<T> for classes that allow applying row based security based on the user's permissions and roles.
  • IEventHandler<T> for classes that respond to a certain business event that has occurred.

这些只是一些抽象示例.这在很大程度上取决于应用程序和设计,您将获得哪些泛型抽象.

These are just a few examples of abstractions. It highly depends on the application and design which generic abstractions you will get.

我编写的应用程序利用了这些通用抽象,这些应用程序只有几个接口,并且只有一个实现.系统中大约90%到98%的模块实现这些通用抽象之一(在某种程度上取决于应用程序的大小;应用程序越大,百分比就越高).

The applications I write make use of these generic abstractions and those applications just have a few interfaces with just one implementation. About 90% to 98% of the modules in the system implement one of those generic abstractions (depending a bit on the size of the application; the bigger the application, the higher the percentage).

这些泛型抽象使在DI库中(或者至少在使用.NET的情况下)在一行代码中注册所有实现变得非常容易,但是更重要的是,正如我之前说过的,应用跨领域关注点变得非常容易.例如,无需对应用程序进行彻底更改,就可以在数据库事务中运行用例,也可以应用死锁重试机制.或者,您可以应用查询缓存,而不必在整个应用程序中进行大范围的更改.

These generic abstractions make it really easy to register all implementations in one line of code in your DI library (or at least, if you are using .NET), but more importantly, as I said before, applying cross-cutting concerns becomes really easy. For instance, without having to make sweeping changes to your application, you can run use cases in a database transaction, or apply a deadlock retry mechanism. Or you can apply caching of queries, without having to do sweeping changes throughout your application.

这篇关于依赖关系反转原则是否意味着我必须为每个模块创建一个接口?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆