依赖注入是否值得?

在博客的世界里进行了一场关于使用依赖注入(DI)之优点和缺点的有趣讨论。论题是:依赖注入是否真的值得?

讨论始自Jacob Proffitt,他撰文解释他的观点说,依赖注入的伸缩性不好。据Proffitt认为,DI流行的唯一原因是Mocking。

DI进来这么流行的真实原因,和正交性、封装性或者其他“纯粹的”架构考量都没有关系。真正的原因是很多开发者都用DI来帮助使用Mock对象进行单元测试。随你怎么说,这个因素实际上说服了聪明的开发者选择DI而不是更简单的实现。

Proffitt甚至声称DI只对单元测试有好处:

不管怎样,我真的希望人们能够承认DI除了单元测试之外,没什么其他有说服力的应用。

不过,Proffitt虽然做单元测试,却不用DI。他使用TypeMock框架。这个框架可以拦截对依赖对象的调用,哪怕依赖是在被测试代码中创建的。这意味着Proffitt不用解耦他的对象也能为单元测试创建Mock。

AyendeRhinoMocks的创造者,他在自己的博客上回应说

虽然能够方便地编写Mock代码是很棒的特性,但这只是主要利益之外的附带好处,主要的利益是降低了对象间的耦合。我可以修改数据访问部分的代码,而不需要触及负责工资计算的引擎,这是我得到的主要益处。

Nate Kohari也回答了Proffitt的原帖。Kohari不但给出了一个DI的代码示例,还详细阐述了什么是真正的DI

如果你是GoF的爱好者,这其实就是Strategy模式。依赖注入(按照我的观点)本质上是大规模使用的Strategy模式。

Kohari是NInject DI框架的作者,他强烈反对DI框架无用的说法:

一旦你开始倚靠DI框架来编写代码,连接对象所需的代价就下降到接近于零。于是,要想达到单一职责的目标,其难度会指数级地下降。

在随后的帖子中,Kohari重申了使用框架的重要性,以此来回应Proffitt原先认为DI的伸缩性不佳的说法:

在真实世界的使用场景中,手工进行的依赖注入的确伸缩性不佳。

Proffitt不同意:

你怎么能说依赖注入(我不是针对整个控制反转模式,但也未尝不可。只是还没轮到。)创造了易于复用的松散耦合的单元?DI本身就要求调用者去提供被调用者的所需。任何理性的评价都会认为这是提高了耦合程度。把耦合的负担丢给框架并不能改变事实,使用一个对象,仍然需要先给它提供外部的东西。

Kohari解释在大多数情况下,如何创建和注射特定类型的对象只需要配置一次,而且是由框架完成的,不是由调用者。

Kohari还谈到了代码的变化能力:

……简单来说,依赖注入让你的代码更容易改变。这就是它在敏捷社群中流行的原因,他们的整个软件开发实践都围绕着快速变化。

Eli Lopian是设计出TypeMock的公司的CTO也加入争论,他对争论的核心有不同的看法:

当你把DI当作是“银弹”来使用,你就丧失了所用编程语言的一大半能力。你不能用静态方法,不能用“new”关键字,不能用封闭类型。哦,你还要把所有的方法都变成虚拟的。

他还争辩说,仅仅为了方便变化而使用DI,违背了YAGNI原则

Lopian继续说:

TDD刚兴起时,首先被讨论的一个问题就是“我们是否应该修改代码来满足可测试的要求?”我们应不应该改变代码的可见性?我们应不应该改变代码的设计?这个问题导致了可测试的代码与OO封装性之间的冲突。开发者们开始为了能够测试,而把代码中的私有部分暴露出来。开头只是私有方法和属性,现在扩大到了整个设计。

这是一个老问题了。有些人认为改变代码让它更容易测试是一件好事;其他人认为这样做打破了封装性,因此是坏事。

Kohari对封装与依赖的的关系提出了看法:

这是让依赖注入物有所值的秘密:当谈到依赖的时候,封装是坏的。

如果出于单元测试的意图而改变代码,能让耦合变得更松散(Proffitt对此有所质疑)——这是不是一件好事呢?

松散耦合与封装都是重要的OO特征,那我们如何作出平衡呢?哪条路才是对的?

查看英文原文:Does Dependency Injection pay off?

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏.NET技术

整理自己的.net工具库

  今天我会把自己平日整理的工具库给开放出来,提供给有需要的朋友,如果有朋友平常也在积累欢迎提意见,我会乐意采纳并补充完整。按照惯例在文章结尾给出地址^_^。

852
来自专栏Java学习网

互联网高手教你如何搜集你想要的信息

  写在前面   几个月前,团队邀我做次内部的分享,主题是如何有效搜索信息。这是因为平时工作中,我经常会分享一些专业学习文档,而这些文档的出现往往很及时,回应一...

3808
来自专栏码神联盟

高效编程所需要做的那点事

聊聊如果才能高效编程 计划(Plan) 所谓Plan,其实就是对应于编程中的设计阶段,当然,这里的Plan并不像设计那样重量级。它要求我们程序员在正式...

2839
来自专栏大数据文摘

去IOE的另外一条路径:全内存数据库弯道超车

2048
来自专栏七夜安全博客

python基础教程第一课

1595
来自专栏北京马哥教育

Linux 内核学习经验总结

学习内核,每个人都有自己的学习方法,仁者见仁智者见智。以下是我在学习过程中总结出来的东西,对自身来说,我认为比较有效率,拿出来跟大家交流一下。

6902
来自专栏ImportSource

听说Redis5.0发布了,那个Streams好叼

在这个商业世界中,如果你占领了概念高地就意味着有种先天的优势。就好比你给自己起了一个名字叫Java。

7993
来自专栏Golang语言社区

一个调度系统的开发与性能优化

背景:随着Go的不断发展,流行度越来越高,业界对Go的认可度也越来越高,所以很多团队或者公司在遇到性能问题时都会尝试使用Go来重构系统,尤其是云计算领域,大家期...

1881
来自专栏Java技术栈

爱上 Java 的10 大理由,Python 弱爆了!

Java和JVM已经存在了很长一段时间了,基于这个事实,一些程序员开始将很多事情视为理所当然。今天我们就来说一说“Java之所以能够成为并将继续是软件项目领先平...

1324
来自专栏云计算D1net

云计算网络应用防火墙提高应用程序的安全性

人们通过云计算网络应用防火墙以确保未在本地托管的应用程序,这是可行的。行业专家马特·帕斯库奇解释它们是如何工作的,以及企业对此所需要了解哪些事情。 如今,网络应...

39611

扫码关注云+社区

领取腾讯云代金券