IoC模式,系统中通过引入实现了IoC模式的IoC容器,即可由IoC容器来管理对象的生命周期、依赖关系等,从而使得应用程序的配置和依赖性规范与实际的应用程序代码分开。其中一个特点就是通过文本的配置文件进行应用程序组件间相互关系的配置,而不用重新修改并编译具体的代码。
例如:工程中业务层类(Business)需要调用底层打印类(RealWriter)来完成自己的功能,关系图如下:
Business类的save方法中会调用RealWriter实例的saveToReal方法,即Business类的功能实现依赖RealWriter类,可如果如今我们又多了一种Writer的实现方式,区别于原来的RealWriter类的功能,此时因为Business直接依赖于RealWriter类,我们无法直接使用既有的Business类了,只能大动干戈地修改Business类,这种情况下,IOC(DI)的思想的优势就体现出来了—“程序不应依赖实现,而是依赖于抽象接口”,于是我们把原来的依赖关系修改一下,如下图:
首先我们定义了一个接口Writer并声明了方法saveToWriter,使原先的RealWriter类和新增的OtherWriter类实现这个接口,将原来Business类中定义的具体类实例RealWriter改成接口Writer的声明,并提供Writer的set方法(set方式注入依赖),在save方法逻辑里直接调用接口的saveToWriter方法,这样我们在使用Business的时候就可以通过注入(set方法)不同的实现类RealWriter,OtherWriter来实现自己的功能。这即是IOC的基本概念所在:通过抽象出对象的接口来实现依赖关系的反转,即将控制权从实际的RealWriter类转移至抽象的Writer接口。当然,Spring是通过配置文件来实现依赖注入的引入的,具体这里就不说明了。
IOC的这种实现方式恰好就是状态(State)模式的完整体现,我们例子中得RealWriter和OtherWriter就是两种不同的状态,利用抽象接口实现不同的状态间的相互切换,便于功能扩展和耦合度的减轻。
具体参考代码如下:
Writer.java
- <span style="font-size:18px;">public interface Writer {
- abstract void saveToWriter();
- }</span>
RealWriter.java
- <span style="font-size:18px;">public class RealWriter implements Writer {
- public void saveToWriter() {
- System.out.println("From the RealWriter");
- }
- }</span>
OtherWriter.java
- <span style="font-size:18px;">public class OtherWriter implements Writer {
- public void saveToWriter() {
- System.out.println("From the OtherWriter");
- }
- }</span>
Business.java
- <span style="font-size:18px;">public class Business {
- private Writer writer;
- public void setWriter(Writer writer) {
- this.writer = writer;
- }
- public void save() {
- System.out.println("Business--save:begin");
- writer.saveToWriter();
- System.out.println("Business--save:end");
- }
- }</span>
依赖注入之所以更流行是因为它是一种更可取的方式:让容器全权负责依赖查询,受管组件只需要暴露JavaBean的setter方法或者带参数的构造子或者接口,使容器可以在初始化时组装对象的依赖关系。其与依赖查找方式相比,主要优势为:<1>查找定位操作与应用代码完全无关。<2>不依赖于容器的API,可以很容易地在任何容器以外使用应用对象。<3>不需要特殊的接口,绝大多数对象可以做到完全不必依赖容器.
依赖注入的基本原则是:应用组件不应该负责查找资源或者其他依赖的协作对象。配置对象的工作应该由IoC容器负责,“查找资源”的逻辑应该从应用组件的代码中抽取出来,交给IoC容器负责。