问题

自动注入到底比new好在哪?

回答
我们常常在编程中遇到这样的场景:某个对象需要使用另一个对象的功能,或者说,它依赖于另一个对象的存在才能完成自己的工作。传统上,我们可能会直接在需要它的地方,用 `new` 关键字亲手把它“造”出来。就像一个厨师,想做一道需要洋葱的菜,就自己跑到菜市场挑最新鲜的洋葱,然后洗干净、切好,放进锅里。

这样做有没有问题?从功能上讲,一点问题也没有。但如果从“如何更优雅、更高效地管理这些相互依赖的零件”这个角度来看,就会发现 `new` 这种“亲力亲为”的方式,虽然直观,却也带来了一些隐性的麻烦。

想象一下,我们有一辆汽车。汽车的发动机需要燃油,而燃油又需要油箱来储存。如果每造一辆汽车,我们都必须手动去给发动机“塞”进一个油箱,并且确保这个油箱里有油,这场景是不是有点滑稽?更别说,如果将来油箱的设计变了,或者我们想换成电动汽车(不再需要燃油),我们就得把所有使用 `new` 出来的油箱的地方都一一修改,这简直是个噩梦。

这就是自动注入想要解决的问题。它更像是在造车的时候,我们只需要告诉汽车“你需要一个油箱,并且能为它提供燃油”,至于这个油箱具体长什么样,在哪儿造,什么时候给它加油,由一个专门的“汽车组装流水线”来负责。当汽车需要油的时候,流水线自然会把加好油的油箱递给它,它根本不用操心这个油箱是怎么来的,也不需要知道油箱的制造过程。

那么,自动注入到底好在哪里,又好在哪里呢?

首先,它极大地简化了代码,提高了可读性。当你的代码里充斥着各种 `new` 的时候,很容易就会被这些“创建”的细节淹没,不知道这个对象的核心逻辑是什么,反而被它的“出生证明”和“养育过程”所干扰。自动注入就像是给代码做了“减法”,把那些琐碎的“制造”工作转移到了一个更集中的地方。你看到的类,它只关心“我需要什么”,而不需要操心“我怎么得到这些东西”。这样一来,代码的关注点就更纯粹,更容易理解它到底在做什么。

其次,也是非常重要的一点,它带来了极大的灵活性和可维护性。回到汽车的例子,如果我们想升级油箱的技术,或者将燃油发动机换成电动马达,我们只需要修改“组装流水线”的部分,告诉它用新的零件代替旧的。而汽车本身的代码,可能完全不需要动一根手指。这意味着,当需求的变更出现时,我们的修改范围被大大缩小了,复用的可能性也大大增加。你不需要去翻遍整个代码库,寻找所有用到 `new` 的地方,然后小心翼翼地逐个修改。

更进一步,自动注入还便于进行单元测试。测试一个对象时,我们通常希望能够隔离它,只关注它的核心功能,而不是它依赖的那些外部组件。用 `new` 的方式,你很难在测试时轻易地替换掉它所创建的依赖。但如果使用自动注入,在测试环境中,你可以很容易地“欺骗”它,让它接收一个模拟的(mock)依赖,而不是真正创建出来的那个。这样,你就可以专注于测试当前对象本身是否按预期工作,而不用担心外部依赖的正确性,极大地提高了测试的效率和准确性。

最后,自动注入也促进了代码的解耦。`new` 的方式,往往意味着一个对象与它所创建的另一个对象之间存在着一种紧密的、硬性的耦合。它们之间的关系是写死的。而自动注入,通过一个中介(比如一个注入容器),使得两者之间的联系变得更加松散。它们之间不再是直接的“你必须在这里出现”的关系,而是“我需要一个符合某种契约的组件,至于谁能提供,由别人来告诉我”的模式。这种松耦合,就像是大家约定好了接口,但具体的实现由大家各自选择,这样整个系统的协同会更加顺畅,也更容易进行调整和扩展。

总而言之,自动注入并不是要否定 `new` 的存在,毕竟在某些简单的场景下,直接创建对象是再自然不过的事情。但当你的系统变得复杂,对象之间的依赖关系也随之增多时,选择自动注入,就像是把一个勤劳却容易在细节中迷失的工人,变成了一个指挥若定的项目经理,它能更宏观、更协调地管理好整个“生产线”,让整个系统运行得更加高效、灵活,也更易于理解和维护。

网友意见

user avatar

讲到底核心就是面向接口编程

user avatar

注入的目的是解耦,当然我们不可能做到完全的解耦,我们只是消减了耦合的强弱,又或者说你把对具体实现类的直接耦合降解为通过依赖接口的间接耦合。而解耦带来了以下的好处:

1. 方便写unit test。因为unit test是一次只关注一个或几个模块,极少全连在一起写测试。于是就要要替换一些做实际操作的模块,比如说写数据库。而如果你把new什么class写死在代码里,这就不太方便unit test来替换它。当然,这里也有别的技术可以mock,但ioc相对来说更简单方便的。

2. 日后升级或修改一个模块更容易。这也是常见的需求,比如说你现在的实现是写入本地文件,你将来可能要写入hdfs,那么只要这两种实现符合同一个接口,那么通过ioc,你将来的升级和替换就可以容易的做到零代码改动。或者你修改一个class的时候,只要不动它的接口,就不会需要改动其他模块。同样的,实现类似的目的你也可以使用其他的设计模式,不过IOC也是其中比较简单易用的一个。

有些朋友提出业务系统里没有也不需要这种可替换模块的设计,因为这会增加的系统的复杂性,我只能部分同意这个观点,现在很多系统的确有些over engineering,接口泛滥,滥用注入等等。所以我同意你说把系统里每一个类都琢磨成可替换的,那这是对ioc的滥用,但你要说写业务系统就轮不到考虑这种灵活性,那就是对系统设计的不负责任,系统的灵活性和可扩展性既不能过分考虑但也不能一点都不考虑,我们要具体案例具体分析,追寻一个平衡。

3. 可以减少依赖关系的复杂度。如果我们都是具体的class依赖其他具体的class,这就很容易形成一个依赖关系网,早晚要造成循环依赖。而如果我们是具体class对接口依赖,而接口又不依赖其他具体class,那么这个依赖关系就是个树或者是一个DAG,就不容易造成循环依赖。

使用ioc也不是没有坏处,比如说就让你的代码实现没有那么直观了,你一下子就看不出调用模块的具体实现是啥。但随着软件系统的复杂度提升,高耦合才是系统设计更关注的问题,这也是IOC等解耦技术会大行其道的原因。


我再补充说一下IOC的其他好处,多谢小石头在评论区的回复,IOC还有几个作用对软件系统的实现很有帮助。

  1. bean生命周期的管理,很多人已经说到了singleton,也有一些lazy初始化的东西,这个不管是spring还是guice都有一些现成的东西可以使用。
  2. 对已有bean或者说class的扩展,这个是Spring早期非常大的亮点,spring当时使用cglib来生成proxy的bytecode,这样不需要额外定义接口,也不需要性能较差的反射就可以实现代理模式,让aop变得非常容易。这也是常见的需求,比如说你要做个权限管理,你就不用一个一个方法的加 if (user.isAdmin())

类似的话题

本站所有内容均为互联网搜索引擎提供的公开搜索信息,本站不存储任何数据与内容,任何内容与数据均与本站无关,如有需要请联系相关搜索引擎包括但不限于百度google,bing,sogou

© 2025 tinynews.org All Rights Reserved. 百科问答小站 版权所有