QCon 演讲火热征集中,快来分享技术实践与洞见! 了解详情
写点什么

依赖注入是否值得?

  • 2007-12-14
  • 本文字数:1552 字

    阅读完需:约 5 分钟

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

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

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

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

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

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

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

虽然能够方便地编写 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?

2007-12-14 04:204424
用户头像

发布了 225 篇内容, 共 65.2 次阅读, 收获喜欢 50 次。

关注

评论

发布
暂无评论
发现更多内容

深度解读Webpack中的loader原理

Geek_02d948

webpack

令人头疼的Javascript隐式&强制转换

hellocoder2029

JavaScript

汇总LED显示屏专业知识

Dylan

LED LED显示屏 led显示屏厂家

某邮储银行数据归集系统在HTAP场景下的选型与实践

OceanBase 数据库

解读Vue3模板编译优化

yyds2026

Vue

Java后端面试到底要如何准备?

钟奕礼

Java 程序员 java程序员 java面试 java编程

为什么vue3要选用proxy,好处是什么?

hellocoder2029

JavaScript

【LeetCode】字母大小写全排列Java题解

Albert

算法 LeetCode 11月月更

深度阐述Nodejs模块机制

coder2028

node.js

BI口碑分析,Quick BI何以连续入选魔力象限

巷子

StartDT奇点云通过CMMI5全球软件领域最高级别成熟度认证

奇点云

CMMI 奇点云

详解webpack构建优化

Geek_02d948

webpack

前端懒加载和预加载

hellocoder2029

JavaScript

腾讯会议后台研发效能提升之路

博文视点Broadview

JavaScript刷LeetCode拿offer-双指针技巧(上)

Geek_07a724

JavaScript LeetCode

用javascript分类刷leetcode3.动态规划(图文视频讲解)

Geek_07a724

JavaScript LeetCode

JavaScript刷LeetCode拿offer-分治

Geek_07a724

JavaScript LeetCode

Vue3, setup语法糖、Composition API全方位解读

yyds2026

Vue

Webpack中的plugin插件机制

Geek_02d948

webpack

深度理解NodeJS事件循环

coder2028

node.js

【11.11-11.18】写作社区优秀技术博文一览

InfoQ写作社区官方

热门活动

双机热备软件原理简单说明以及品牌重点推荐

行云管家

高可用 ha 热备 双机热备

深聊Nodejs模块化

coder2028

node.js

技术公开课|深度剖析 Java 的依赖管理,快速生成项目 SBOM清单

墨菲安全

Java SBOM 软件供应链安全

用javascript分类刷leetcode3.动态规划(图文视频讲解)

js2030code

JavaScript LeetCode

前端leetcde算法面试套路之回溯

js2030code

JavaScript LeetCode

React中常见的TypeScript定义实战

xiaofeng

React

React-hooks+TypeScript最佳实战

xiaofeng

React

Vue.nextTick核心原理

yyds2026

Vue

腾讯大神亲码Redis技术笔记,实战+原理双管齐下

小小怪下士

Java redis 程序员 腾讯

前端leetcde算法面试套路之堆

js2030code

JavaScript LeetCode

依赖注入是否值得?_.NET_Niclas Nilsson_InfoQ精选文章