我正在使用依赖注入。实际上,这涉及UI层(例如,Web应用程序),其包括DI容器,其具有关于它将使用的接口的一大堆数据,以及用于每个的实现。
但是,由于需要满足依赖性,DI容器还需要知道它本来不需要访问的接口和类。例如:
UI层需要使用IWidgetManager
接口。配置的实现是ConcreteWidgetManager
。这是在DI容器中注册的。
ConcreteWidgetManager
依赖于IWidgetRepository
。所需的实现是ConcreteWidgetRepository
。因此,DI容器还必须知道IWidgetRepository
和ConcreteWidgetRepository
。
如果我只是简单地编码ConcreteWidgetManager
对ConcreteWidgetRepository
的依赖关系,那么ConcreteWidgetRepository
可以在内部生成,因此我的UI图层不可见。没有UI代码可以绕过管理器层并直接使用存储库层。
因此,似乎使用DI虽然在很多方面都是一个很棒的模式,但从UI层的角度来看却无法实现封装。
我是否正确地思考这个问题,有没有办法缓解这种情况?
答案 0 :(得分:2)
您描述这种方式意味着UI层负责创建DI容器。
在您的特定应用中可能会出现这种情况。但请记住,初始化部分只是整个代码的一小部分。
是的,有一些代码以某种特定顺序创建DI容器和UI层。可能是UI层调用函数CreateDIContainer
来初始化所有组件。但是这个函数是提到实现的唯一实例; UI层的所有其他方面处理抽象接口。纯粹主义者可能会遇到问题,但实际上,在非CreateDIContainer
的99.5%的代码中,UI不知道实现是什么。将初始化函数移动到单独的库中的单独静态类中,如果这样可以让您更快乐。
<小时/> 我也对你说
我只是硬编码ConcreteWidgetManager对ConcreteWidgetRepository ....的依赖,
你提出它作为一个选项让我想知道:你有什么理由不硬编码这种关系吗?我必须使用依赖注入 - 最终的好处是我可以在自动化测试中模拟部分代码。
你想要嘲笑ConcreteWidgetRepository
吗?如果你不是,那么继续硬编码关系。否则你只是为了架构而引入架构。
答案 1 :(得分:2)
不,依赖注入不破坏封装。这是您打破封装的应用程序分层方式。通过分离不同程序集中的接口和实现并将容器配置放在自己的程序集中,可以防止UI层必须依赖于具体的ConcreteWidgetManager
,甚至IWidgetRepository
如果你愿望。
但是,添加额外的组件会产生成本。在维护和及时编译解决方案。我不是使用程序集强制执行架构规则,而是使用明确而简单的指南强制执行它们,或者使用NDepend等工具强制执行它们。但是,当团队规模足够小时,使用代码审查将足以防止架构侵蚀。
答案 2 :(得分:1)
是的,它打破了封装。但这是使用DI需要支付的价格。它使您的代码更可测试但是打破了封装。
从类的API角度来看,要求注入依赖项的类变得尽可能愚蠢,但调用此类的类现在知道得太多了。在你的情况下框架。如果你在代码中使用DI,比如构造函数注入,那么调用类需要知道几个类。