to boyszz:
是否能够这样理解你的话--ioc有很多好处,但它并不能够真正降低耦合。

我一直对这个问题有疑问,因此我想得到清楚的解释和明确的答复,因为我认为理解错误还不如不理解。

假设Map是你的类需要使用的一个接口,当你的类写过
private Map map = new HashMap();
这样的代码的时候(我想你应该不会写出private HashMap map = new HashMap();这样的代码),你的这个类就是和HashMap这个实现类紧耦合了.以后你修改其实现的时候(例如需要将HashMap换成线程同步的Hashtable),你就需要四处修改你的代码.这是因为你的类和HashMap紧耦合造成的,理想的情况你的类只应该知道有一个Map接口提供给他服务,具体是什么实现类去实现,怎么实现他并不应该关心.你的类中只应该出现这样的代码.
private Map map;
public Map setMap( Map map ) {
this.map = map
}
public Map getMap() {
return map;
}
这样你的类就没有跟任何Map的实现类紧耦合.IOC容器会在运行时通过配置文件来进行装配他的实现类.
耦合不光可以从一个类的方面理解,还可以从一个层的方面理解,想想DAO吧,他的存在难道不是为了降低持久层和业务层的耦合么?可是如果在你的业务层代码中使用了
MyDao myDao = new MyDaoDBImpl();
这样的代码,那Dao的功能就仅仅是帮你做到了持久层和业务层的分离,你的业务层依然需要清晰的知道持久层的实现类具体是哪个.造成了你的Dao实现类和你的业务层紧耦合现象.理想情况是,你的业务层只需要知道有一个Dao接口可以为他提供服务,具体是哪个,他不需要关心.我相信,好的Dao实现层都是应该通过IOC或者与IOC类似的概念去实现的.
个人观点,欢迎探讨.

严重同意lz的观点。IoC只是把各层之间的依赖关系汇总到配置文件中,方便管理和修改;没有配置文件,各层之间的Bean则没有关系。其实没有降低耦合,真正降低耦合还在于应用系统的设计!

首先需要明确一点,没有耦合的类可能什么也做不了.
还是之上的那个例子,你的类需要依赖Map接口提供的服务,这样你的类就肯定是要耦合Map接口的.你希望通过设计,让你的类可以不依赖Map接口?下次用List接口替换也一样可以用吗?这恐怕是不现实的吧.接口就是为了隐藏实现类的细节,解除调用者和接口实现类的耦合而存在的,IOC可以赋予接口这样的能力.
写过Servlet吧?HttpServletRequest你一定也用过,但你在用的时候知道HttpServletRequest的实现类是哪个吗?你不知道,因为是Web容器自动为你注入了HttpServletRequest实现类.如果不是,那你在Tomcat下的Servlet就可能需要写这样的代码,HttpServletRequest request = TomcatUtil.getServletReqeust();而当你的应用因为扩展,需要移植到WebSphere的时候.你可能要把你所有以上的代码都修改一遍,换成WebSphere的HttpServeltRequest的实现.这样的情况你肯定不愿意发生.
你的Servlet是不是因为不用知道HttpServletRequest这个接口的实现类而把耦合给降低了呢?至少他更换WEB容器的时候不用再修改这部分代码.IOC做的也是一样的事情,他让你的类不用知道接口服务的实现类具体是哪个,那他不是在降低耦合?
[该贴被boyszz于2007年09月09日 20:48修改过]

>> 假设Map是你的类需要使用的一个接口,当你的类写过
>> private Map map = new HashMap();
>> 这样的代码的时候(我想你应该不会写出private HashMap map = new HashMap();这样的代码),你的这个类就是和HashMap这个实现类紧耦合了.以后你修改其实现的时候(例如需要将HashMap换成线程同步的Hashtable),你就需要四处修改你的代码.这是因为你的类和HashMap紧耦合造成的,理想的情况你的类只应该知道有一个Map接口提供给他服务,具体是什么实现类去实现,怎么实现他并不应该关心.你的类中只应该出现这样的代码.
>> private Map map;
>> public Map setMap( Map map ) {
>> this.map = map
>> }
>> public Map getMap() {
>> return map;
>> }
>> 这样你的类就没有跟任何Map的实现类紧耦合.IOC容器会在运行时通过配置文件来进行装配他的实现类.
>> 耦合不光可以从一个类的方面理解,还可以从一个层的方面理解,想想DAO吧,他的存在难道不是为了降低持久层和业务层的耦合么?可是如果在你的业务层代码中使用了
>> MyDao myDao = new MyDaoDBImpl();
>> 这样的代码,那Dao的功能就仅仅是帮你做到了持久层和业务层的分离,你的业务层依然需要清晰的知道持久层的实现类具体是哪个.造成了你的Dao实现类和你的业务层紧耦合现象.理想情况是,你的业务层只需要知道有一个Dao接口可以为他提供服务,具体是哪个,他不需要关心.我相信,好的Dao实现层都是应该通过IOC或者与IOC类似的概念去实现的.

boyszz的观点我同意,不过你举的这个例子却有可商榷之处。

如果一个类需要定义一个private的类型是Map的实例变量,那么可能最好的方式缺省是:

private HashMap map = new HashMap();

为什么呢?因为:

1. 如果这个类有一个Map类型的private的实例变量,那么几乎可以肯定对这个Map操作的主要是这个类(否则的话,就是类的封装没有做好),既然如此,有这个类来确定Map的真正类型(或者实现)是最合理的,也就是说,这个类有责任确定Map的实现,而不是其它类。

2. 之所以写成上面的方式,而不是private Map map = new HashMap();是因为这样能够让JVM进行最好的运行是优化,而不必担心多态的问题。

所以,并不是什么东西都要写成接口,也不是什么东西都要Ioc,在使用它们之前,先问一问自己为什么。

>> 写过Servlet吧?HttpServletRequest你一定也用过,但你在用的时候知道HttpServletRequest的实现类是哪个吗?你不知道,因为是Web容器自动为你注入了HttpServletRequest实现类.如果不是,那你在Tomcat下的Servlet就可能需要写这样的代码,HttpServletRequest request = TomcatUtil.getServletReqeust();而当你的应用因为扩展,需要移植到WebSphere的时候.你可能要把你所有以上的代码都修改一遍,换成WebSphere的HttpServeltRequest的实现.这样的情况你肯定不愿意发生.
>> 你的Servlet是不是因为不用知道HttpServletRequest这个接口的实现类而把耦合给降低了呢?至少他更换WEB容器的时候不用再修改这部分代码.IOC做的也是一样的事情,他让你的类不用知道接口服务的实现类具体是哪个,那他不是在降低耦合?

HttpServletRequest好象不是所谓的注入的吧,只是作为参数传递进来而已,和IOC完全没有关系,就是简单的面向接口编程。

我丝毫不会怀疑面向接口在降低耦合方面的优雅和强大,如果你说依赖注入只是为了更好的实现它,那我理解,但如果你说依赖注入是另一个不同的东西,我就不懂了。

private Map map = new HashMap();

以这个为例,如果有一天要用hashtable实现,只好把后面改为new hashtable(),若是ioc的话,我们也必须在配置文件里做出相应更改,我承认,后者更加规范更加优雅,但也仅此而已。我们要使用的map接口都最终要实现,而更改它的实现都逃不过改变它的实现类,所以,除去接口所产生的影响,ioc到底在哪里降低耦合了?

各位可能讨论得比较散了。重点前提必须了解工厂模式Factory模式。

Ioc其实是工厂模式得升级,我在一篇文章说过。首先明白工厂模式解耦性: 将对象使用和对象创建进行分离。

如果说工厂模式还不能彻底解决耦合,因为客户端会和工厂类耦合,那么IOC则进一步了,如果客户端和被调用者都在IOC容器内,则客户端就只和具体被调用者得接口耦合,OO中同步系统目前做到和接口耦合就算是松耦合了(JMS等异步则完全解耦)。

>> 如果说工厂模式还不能彻底解决耦合,因为客户端会和工厂类耦合,那么IOC则进一步了,如果客户端和被调用者都在IOC容器内,则客户端就只和具体被调用者得接口耦合,OO中同步系统目前做到和接口耦合就算是松耦合了(JMS等异步则完全解耦)。

你说的耦合只是技术上的耦合,其实真正的耦合是业务的耦合,业务接口的耦合。

to banq:
那是否意味着,工厂模式是把散落各处的接口的实现集中到一个类中,而ioc又进一步,它把实现都放在一个配置文件,但这些都是优化,没有本质的区别。


我赞成这句话“真正的耦合是业务的耦合”

可能你误解了我的意思,HttpServletRequest确实与IOC没有什么关系,我的意思是你的Servlet代码里不存在HttpServletRequest的实现类,这样,你的Servlet就是与HttpServletRequest实现类解耦的.但是如果你在别的层要实现这样的解耦,你只能通过IOC不是么?
IOC分两种.一种是依赖查找(例如JNDI),第二种是依赖注入(例如Spring的IOC容器).两者的区别是类是否需要自己查找服务对象.如果你希望一个类与为他服务的实现类解耦,例如你的DataSource对象,你在你所有的Dao实现类里写上private DataSource = new DataSourceImpl( arg0, arg1 );(只是假设一下)这样的代码吗?当以后你的应用要修改DataSource的实现,是否需要修改所有的Dao类?如果一个项目很大,这就不是一项小工作量,造成这个原因就是因为你所有的Dao实现类是于DataSourceImpl这个实现类紧耦合的.
通常,你可能把private DataSource = new DataSourceImpl( arg0, arg1 );这段代码抽到一个工厂类的静态方法中,然后在你所有的Dao实现类中写上DataSource source = Factory.getDataSource();如果你为了避免硬编码,你就有可能把一些数据库配置通过配置文件的方式读取.如果以后要修改实现,你只需要修改Factory中的一个静态方法而已.但这其实就是IOC的第一种依赖查找,只是你自己在实现IOC而已,IOC只是一个概念,并不是只有使用了Spring.picContainer之类的IOC容器,你才叫使用IOC.通过类反射,工厂类模式你完全可以实现自己的IOC,你实现的目的也只是为了解耦.

>而ioc又进一步,它把实现都放在一个配置文件,但这些都是优化,没有本质的区别。

不是,配置只是一个表象,我们不能看Spring的IOC来理解IOC,因为Spring缺省的IOC我已经反复批判过,不是auotwiring,在autowiring情况下,配置只是每个实现的名称和类,以Jdon框架配置为例子如下:
<component id="a" class="某个实现类A"/>
<component id="b" class="某个实现类B"/>

在auotwiring+Annotation情况下,我们甚至无需这些配置,我们只要在代码实现中加入Annotation就可以,比如在某个实现类A中加入Annotation:
@id="a"
public class A{

}

所以,配置不只是优化,而是升华,如果整个Web容器都支持IOC,我们甚至无需写Annotation,就象使用Servlet一样使用Web Beans。

IOC主要针对接口,关于业务解耦问题,而业务主要是一些抽象,不属于接口范畴,所以IOC无法适用,业务解耦可以参考Evans DDD.

保留意见,继续思考,再说就要重复了,谢谢各位~
[该贴被bsglz于2007年09月10日 14:50修改过]

学习了,谢谢各位分享自己的见解。

学习