依赖注入走多远?

Wal*_*rey 5 dependency-injection solid-principles

最近发现了依赖注入,我现在正试图掌握使用它的频率和距离。

例如,假设我有一个对话框,提示用户输入他们的注册详细信息——名字、姓氏、电话号码、序列号——诸如此类。应以各种方式验证数据(例如,名字和姓氏不为空,序列号为特定长度)。一旦通过验证,它应该被缓存在本地机器上,并发送到注册服务器。只有在所有这些事情都成功或用户取消后,该对话框才应关闭。

所以这可能是我们在这里尝试实现的四件事(职责):UI、验证、本地缓存、将数据发送到非本地服务器。

对话的职责是什么,应该注入什么?显然对话框是 UI,但是验证、缓存和数据发送都应该被注入吗?我认为他们这样做,否则对话框类必须知道数据字段背后的逻辑才能进行验证,它必须知道如何以及在何处缓存数据,以及如何将数据发送到某个地方。如果是这样,这可能会导致调用者端出现一些繁重的代码(假设我们通过构造函数进行注入,我认为这比 setter 函数更可取),例如

MyDialog dlg(new validator(), new cacher(), new sender());
Run Code Online (Sandbox Code Playgroud)

但也许这没问题?在多年看到诸如对话框之类的东西做所有事情的代码之后,它现在对我来说确实有点陌生。但我也可以看到这种情况如何迅速升级——如果还有其他各种各样的小事情需要做——有多少被注入的东西变得“太多”了?

请不要试图在示例场景中挑漏洞,我只是用它来说明。我对 DI 的原则更感兴趣,以及在什么时候你可能会走得太远。

Mat*_*ton 1

嗯,你当然可以做到。注入验证很有意义,因为这样您就可以围绕验证代码编写单元测试,而无需启动任何 GUI 组件即可工作。注入缓存是有意义的,因为这样对话框就不必了解其界面之外的有关缓存系统的任何信息。注入发送者很有意义,因为您的对话不必对任何事情的进展有最模糊的了解。

我有把事情分开的习惯,因为我喜欢单一责任原则,而且我喜欢编写尽可能纯粹的代码。

问题是,当您注入太大的接口时,您不再有任何合理的想法,您所注入的东西可能实际上需要调用这些接口的哪些部分,并且交互变得复杂,您的单元测试开始依赖确切地说,依赖项会做什么,因为当您知道 75% 的接口不会被使用时,您就不会费心去模拟整个接口。

因此,一定要注入明显独立的职责的东西,但要确保以适当约束的方式设计它们的接口。类可以同时实现多个接口,因此您并不是不能将接口分割成小块,而是可以根据需要使用同一个对象来实现它们。依赖代码永远不必知道!

至于什么时候你走得太远了……真的很难说,但我认为你不会达到这一点,直到你使用一个根本不添加任何东西的界面注入一些东西。我总是想注入有副作用的东西,因为这对单元测试和保持事情更加合理有很大的帮助。如果您可以将业务逻辑拆分为纯类并注入它们,那么您将在为其编写单元测试时度过一段美好的时光,因此这可能是值得做的。

我使用这样的测试:

  1. 它是否执行 I/O 而我尚未进入 I/O 提供类?注射它。
  2. 它是否提供了我不需要知道细节的独立处理?注射它。
  3. 它所做的事情是否不属于我的单一职责?注射它。

你的旅费可能会改变。