依赖注入真的能够降低耦合吗?

接触依赖注入有一段时间了,对于它的好处基本上都是这样说:“bean自己不再负责对象的依赖关系,从而降低对象之间的耦合。同时让开发者把精力放在业务逻辑的编写上。”
但我总怀疑是否真的降低了耦合,因为我们在获取想要的bean的时候还是一样的通过一个唯一的标识,对于既有的设计而言,这样做只是把存在耦合的地方汇总起来,方便我们查看更改。
我觉得想要降低耦合,只能通过良好的建模,以及之后的重构。

个人观点,有理解不到位或者错误的地方,请不吝赐教。

关键是如何理解“耦合”这个词:

Interface bi;
Class b implements bi;

1. Class a 中,Class bi = new b();
a 和 bi是编译时耦合。
a 和 b 之间是编译时耦合。

2. Class a 中,Class bi = Class.forName("b").newInstance();

a 和 b 是运行时耦合,但是只是推迟了耦合的时机,和 1 没有本质区别。

3. Class a 中,Class bi = Class.forName(bname).newInstance();
bname从外部配置文件中或者参数中获取。

a 和 b 是潜在运行时耦合,你可以用类c替换b而不会影响a。

IoC的效果和 3 一样。

对于耦合关系的设计并不是越低越好,也不是越高越好,而是必须将设计中的耦合关系和业务模型中的耦合关系保持一致。

当业务模型中a必须依赖b的时候,最好的设计是1,这个时候代码最清楚、简洁,也最直接的表达出它们之间的耦合关系,如果用3反而使得a无法理解。

当业务模型中a是依赖于bi的某一种实现的时候,最好的设计是3。

注意,在设计过程中,解耦不是目的,让bi的实现可以不影响a而变化(前提是bi的实现确实存在潜在的变化可能)是目的。

使用IoC也一样,不要把所有的依赖关系的分离,这样你的设计将让人无法理解,包括你自己。

你的例3中“a 和 b 是潜在运行时耦合,你可以用类c替换b而不会影响a。”
我觉得这是面向接口带来的好处,在例1中用new c() 代替 new b()不也是可以的吗,假设c和b都实现了bi接口。

>你的例3中“a 和 b 是潜在运行时耦合,你可以用类c替换b而不会影响a。”我觉得这是面向接口带来的好处,
说对了,其实所谓IoC就是接口实现分离的一种使用方式而已(当然不局限于接口,基类也行)。

>在例1中用new c() 代替 new b()不也是可以的吗,假设c和b都实现了bi接口。
如果你愿意修改代码的话,一切都是可能的。

但是面向接口好象是在代码中实现的,正如你上面的例子,而我们通过name从容器中获取bean的时候还是对应的具体的class。
所以,在我看来,依赖注入所做的只是把代码中需要修改的地方挪到了配置文件中。

>但是面向接口好象是在代码中实现的,正如你上面的例子,而我们通过name从容器中获取bean的时候还是对应的具体的class。
>所以,在我看来,依赖注入所做的只是把代码中需要修改的地方挪到了配置文件中。

是的,完全正确。你不可能消除依赖(对接口、对类),但是你可以用不同的方式来实现依赖。如果a在运行的时候要依赖b的时候,一定在某个地方你可以看到这个依赖关系,只是这个关系的表现各不相同而已。

“bean自己不再负责对象的依赖关系,从而降低对象之间的耦合。同时让开发者把精力放在业务逻辑的编写上。”
那么这句话以及平时看到的很多类似的说法是否对依赖注入过分夸大了?

>“bean自己不再负责对象的依赖关系,从而降低对象之间的耦合。同时让开发者把精力放在业务逻辑的编写上。”
>那么这句话以及平时看到的很多类似的说法是否对依赖注入过分夸大了?

是的,你当然应该关心业务逻辑之间的耦合关系。但是关键在于你不要将不应该耦合的对象耦合在一起,也不应该将该耦合的对象分开,同时你还要考虑在可以预料到的将来,耦合关系会如何变化。

对象之间的依赖关系是面向对象设计过程中非常重要的内容(OO设计主要两件事情,一是如何设计对象,二是如何设计对象之间的关系)。

呵呵 我的看法跟你几乎完全一致。
谢谢费心费时答疑!

建议你看一看《敏捷软件开发-原则,模式,实践》这本书。

恩,会找时间去研究下的,谢~

>但我总怀疑是否真的降低了耦合,因为我们在获取想要的bean的时候还是一样的通过>一个唯一的标识
那是因为是从IOC容器外部获得bean(也就是IOC得不彻底),如果整个Web容器就是一个Ioc容器,就无需了,看看JBoss SEAM得IOC就已经接近了,所以有Web Bean标准。

>>那是因为是从IOC容器外部获得bean(也就是IOC得不彻底),如果整个Web容器就是一个Ioc容器,就无需了,看看JBoss SEAM得IOC就已经接近了,所以有Web Bean标准。

那是因为依赖关系变成容器中的Autowiring了。无论任何情况,你还是必须知道Autowiring的基本原则,其实说到底就是将其他的依赖关系(语言的或者配置的或者其它)转换成约定的依赖关系(Autowiring规则)而已。

对,我也是这么觉得,ioc所能做的只是方便管理,并没有真正的降低耦合。

banq老大说的应该是“约定优于配置”的意思吧,我对之前对这个概念没有多少了解,字面上看,应该是说将之前需要配置的一些部分换成大家的约定,从而减少多余的配置文件,在网上查了下,也大概是这个意思,如果是这样的话,那它也只是对配置的形式做了改变,并没有实质性的不同。

IOC有三个好处:
一个是可以把一些参数的配置通过一个统一的IOC容器来进行管理(你的开发团队应该不会在一个项目中使用两个IOC容器的),比如说你的一个发送邮件服务对象所需要的SMTP服务器,用户名,密码之类的参数.优点是不用在类里进行硬编码,不会导致团队中每个开发人员都有自己的配置文件,还有一个自己的配置文件读取帮助类.一个项目中,执行相同功能的类有多个存在有何意义?
第二个是在接口编程中,IOC帮你消除了InterfaceA a = new AImpl();这样的代码.也许你会将所有服务对象的实例化操作都放在一个工具类(例如ServiceFactory)中,然后通过这个工具类来进行服务对象的获取.但当你替换实现时还是需要改动你的代码.假设你的一个模块在另一个项目中进行了复用,但复用的前提是你需要修改AImpl,这时候你愿意保存两份AImpl代码,针对不同的项目修改不同的AImpl,还是给另一个应用开发一个AImpl2,然后修改你的ServiceFactory,保存两份ServiceFactory代码?我想你会选择使用IOC的配置文件方式的.
第三个是方便测试,这就不多说了