依赖注入将业务层内部暴露给 UI 层——这肯定是一件坏事吗?
我目前正在处理依赖注入。实际上,这涉及 UI 层(例如 Web 应用程序),其中包括一个 DI 容器,该容器包含大量有关它将使用的接口的数据,以及每个接口使用的实现。
然而,由于需要满足依赖关系,DI 容器还需要了解它不需要访问的接口和类。例如:
UI 层需要使用 IWidgetManager
接口。配置的实现是ConcreteWidgetManager。这是在 DI 容器中注册的。
ConcreteWidgetManager
依赖于 IWidgetRepository
。所需的实现是ConcreteWidgetRepository。因此,DI 容器还必须了解 IWidgetRepository
和 ConcreteWidgetRepository
。
如果我只是硬编码 ConcreteWidgetManager
对 ConcreteWidgetRepository
的依赖,那么 ConcreteWidgetRepository
就可以成为内部的,因此对我的 UI 层不可见。没有 UI 代码可以绕过管理层并直接与存储库层一起工作。
因此,尽管 DI 的使用在许多方面都是一种很棒的模式,但从 UI 层的角度来看,它似乎打败了封装。
我的想法正确吗?有什么办法可以缓解这种情况吗?
I'm working with dependency injection at the moment. Effectively this involves the UI layer (e.g. a web application) including a DI container which has a whole bunch of data about interfaces it will work with, and which implementation to use for each.
However, because of the need to satisfy dependencies, the DI container also needs to know about interfaces and classes it would not otherwise need access to. For example:
The UI layer needs to work with an IWidgetManager
interface. The configured implentation is ConcreteWidgetManager
. This is registered in the DI container.
The ConcreteWidgetManager
has a dependency on IWidgetRepository
. The desired implementation for this is ConcreteWidgetRepository
. Thus the DI container must also know about IWidgetRepository
and ConcreteWidgetRepository
.
Had I simply hardcoded ConcreteWidgetManager
's dependency on ConcreteWidgetRepository
, then ConcreteWidgetRepository
could be made internal and therefore invisible to my UI layer. No UI code could bypass the manager layer and work directly with the repository layer.
Therefore it seems that the use of DI, although an awesome pattern in many respects, defeats encapsulation from the point of view of the UI layer.
Am I right in thinking this, and is there any way to mitigate the situation?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
不,依赖注入并没有破坏封装。这是破坏封装的应用程序分层方式。通过将不同程序集中的接口和实现分开,并将容器配置放入其自己的程序集中,您可以防止 UI 层必须依赖于具体的
ConcreteWidgetManager
,甚至是IWidgetRepository如果你愿意的话。
然而,添加额外的组件是有成本的。无论是维护还是编译解决方案所需的时间。我不会使用程序集来强制执行体系结构规则,而是使用清晰且简单的指南或使用 NDepend 等工具来强制执行它们。然而,当团队足够小时,使用代码审查将足以有效地防止架构侵蚀。
No, Dependency Injection is not breaking encapsulation. It is the way you layered your application that is breaking encapsulation. By separating the interfaces and implementations in different assemblies and putting the container configuration in its own assembly, you can prevent the UI layer from having to take a dependency on the concrete
ConcreteWidgetManager
, or even theIWidgetRepository
if you wish.However, adding extra assemblies has cost. Both in maintenance and in time it takes to compile your solution. Instead of enforcing architectural rules using assemblies, I would enforce them with clear and simple guidelines, or perhaps with a tool such as NDepend. However, when the team is small enough, using code reviews will be effective enough to prevent architectural erosion.
您描述这一点的方式意味着 UI 层负责创建 DI 容器。
这在您的特定应用程序中可能是正确的。但请记住,初始化部分只是整个代码的一小部分。
是的,有一些代码以某种特定的顺序创建 DI 容器和 UI 层。 UI 层可能会调用 CreateDIContainer 函数来初始化所有组件。但这一个函数是唯一提到实现的实例; UI 层的所有其他方面都涉及抽象接口。纯粹主义者可能会提出问题,但实际上,在 99.5% 不是
CreateDIContainer
的代码中,UI 不知道实现是什么。如果这让您更高兴,请将初始化函数移至单独库中的单独静态类中。I'm also intruiged about you saying
你把它作为一个选项提出来让我想知道:你有什么理由不硬编码这种关系?我在必要时使用依赖注入 - 最终的好处是我可以在自动化测试中模拟部分代码。
您想要模拟
ConcreteWidgetRepository
吗?如果不是,那么请继续对关系进行硬编码。否则你只是为了架构而引入架构。(请参阅您不需要它。)
The way you're describing this implies that the UI layer is responsible for creating the DI container.
This might be true in your particular application. But remember that the initialization part is a tiny part of the overall code.
Yes, there is SOME code that creates the DI container and the UI layer in some particular order. It might be the case that the UI layer invokes a function
CreateDIContainer
which initializes all of the components. But this one function is the only instance where the implementations are mentioned; all other aspects of the UI layer deals with the abstract interfaces. A purist might take issue, but really, in the 99.5 percent of the code that ISN'TCreateDIContainer
the UI does not know what the implementations are. Move the initialisation function into a separate static class in a separate library if that makes you happier.I'm also intruiged about you saying
That you brought it up as an option makes me wonder: do you have any reason NOT to hardcode this relationship? I use dependency injection when I have to - the end benefit is that I can mock parts of the code away in my automated tests.
Are you going to want to mock out the
ConcreteWidgetRepository
? If you are not, then go ahead and hardcode the relationship. Otherwise you're just introducing architecture for the sake of architecture.(See You Aren't Gonna Need It.)
是的,它确实破坏了封装。但这就是使用 DI 需要付出的代价。它使您的代码更具可测试性,但会破坏封装。
从类的 API 角度来看,要求注入依赖项的类变得尽可能愚蠢,但调用此类的类现在知道太多了。在你的情况下框架。如果您在代码中一般使用 DI(例如构造函数注入),则调用类需要了解多个类。
Yes, It does break encapsulation. But that's the price you need to pay for using DI. It makes your code more testable but yes breaks encapsulation.
From an API perspective of a class, your class that asks for dependencies to be injected becomes as dumb as possible but the class that calls this class now knows too much. In your case the framework. If you use DI in general in your code in terms of say, constructor injection, you calling class needs to know about several classes.