这两天学习设计模式,看到了关于依赖反转方面的东西,就依赖反转而言简单来说就是:软软设计存在抽象层和实现层之分,一般情况下是抽象依赖于实现,这很容易理解,但是问题在于抽象层相对稳定,实现层容易变化,抽象层应该具有较实现层更高的复用性,它就应该我们设计和考量的核心与重点,而不是具体的实现,这样一来,要求我将依赖关系反转过来,即实现应该依赖于我们的抽象,这样的设计才是灵活和易于扩展的。如此一来,依赖反转的概念就出来了,但是如果实现依赖反转呢?答案听上去是很简单的那就是基于接口进行编程,关于这一点在许多讲述设计模式的书中都有所介绍,接口是对类和方法的一组需求定义,由于接口不提供任何实现,它是一种最纯的抽象,基于接口进行编程可以真正意义上实现在抽象层上进行工作。但是还有一个恼人的小问题:
interface IService
{
public void doSomething();
}
class ServiceImpl implements IService
{
public void doSomething()
{
// do something......
}
}
class Component
{
private IService service=new ServiceImpl(); //这里是基于接口进行编程,但却在构造时与具体的实现发生了耦合
//后继方法中会使用该service
}
问题就出在对service的实例化上,我们可以明确的看到接口service在实例化时要不可避免地使用到它的实现类,从而导致了 service至少在创建时刻对具体实现产生了依赖,这使得我们之前打算的基于接口进行编程的工作似乎功亏一篑。在一些设计模式的书中对于这一问题的解决方法是使用工厂模式,来实现对service实例化的封装。
呵呵,写到这里禁不住要偷笑了,因为晚上在思考这个问题的时候实然想到了spring的依赖注入,不禁恍然大悟:不需使用什么工厂模式,运用spring的依赖注入会完美地解决“抽象在创建时刻对实现产生依赖”的问题,在我们代码里,抽象将永远不需要再被指向他的具体实现了,这个过程会放到sping 的配制文件里,使用依赖注入的方式完成,这样了,我们真得就可以完美使用接口进行编程了!
以下内容是2010-5-8的补充:
在我刚开始接触spring和IOC理论时,最常见的IOC示例就是Service依赖DAO时,在Service的实现类里声明一个DAO字段,通常这个DAO是一个接口,然后提供一个setter,就在Service的方法中开始使用这个DAO了,整个Service没有实例化这个DAO的操作。其实这个示例展示了两点:一个是依赖注入,另一个是基于抽象编程。我当时一直认为:只有在基于抽象编程的前提下,进行依赖注入才是有意义的。否则声明的是一个具体类,再注入一个具体类有什么意义呢?这是我三年前对IOC的认识。
后来,我研究一个IOC框架PicoContainer:http://www.picocontainer.org/,这个IOC构架的参考文档让我对IOC有了更进一步的认识,也使我发现了我过去对IOC认识的一个局限。本质上讲,IOC之所以如此重要,是因为它解决了OO技术中一个重要的缺失:如何合理地创建对象并将它们关联起来协同工作。面向对象技术注重于对对象属性与行为的模拟,对这些对象从何而来,怎样相互依赖缺乏合理的模拟机制。很多时候,当一个对象A依赖到另一个对象B时,我们就new一个B的实例出来。这样做在有些时候是合理的,有些时候就是不恰当的。上面提及的基于抽象编程时就是一种情况。还有其他的情况。比如:A有没有创建B的责任?如果A没有创建B的责任,那么就不应该让A来承担。这是OO设计的一个重要原则:Designing Objects with Responsibilities。关于这一点,《Applying UML and Patterns,3rd Edition》一书17.10节有详尽的说明。
总之IOC就像一个系统的对象容器和粘合剂,它负责创建对象的实例,并按照它们声明的依赖关系把它们粘合起来共同工作。