面向对象设计(OOD)之IoC/DI
程序设计中都将要面向对象编程(OOP),面向对象设计(OOD),我们最根本的目的是设计出易扩展,可插拔,健壮的架构,写出"高内聚,低耦合"的好代码。
最近在看《跟着开涛学SpringMVC》的时候,经常会涉及到几个概念:IoC/DI,而且这些个概念是spring的核心。
1.如何定义IoC/DI?
IoC(invers of control控制反转):它是一种 软件设计模式,它告诉你应该如何做,来解除相互依赖模块的耦合。
DI(dependency inject依赖注入):它提供一种机制,将需要依赖(低层模块)对象的引用传递给被依赖(高层模块)对象。
2.IoC/DI之间有什么关系?
其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,相对IoC 而言,“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。
3.IoC/DI有什么好处?
我们举例说明:在开发电商系统时订单入库比较依赖于我们数据库的类型,假如现在我们使用的是mysql数据库
MySQL操作类用于数据库的读写:mysqlDal.java
 
public class mysqlDal{ public void add(){ system.out.println("入库成功!"); } }
order处理类,负责订单的逻辑处理。由于订单要入库,需要依赖于数据库的操作。因此在Order类中,我们需要定义mysqlDal类的变量并初始化。
 
public class Order { private mysqlDal dal = new mysqlDal ();//添加一个私有变量保存数据库操作的对象 public void Add() { dal.Add(); } }
下面我们测试一下:
 
public class Program { static void Main(string[] args) { Order order = new Order(); order.Add(); } }
运行结果是:入库成功!
某一天接到通知,全线切换到oracle数据库,好吧,我们坑吱坑吱的把上面的代码重新改一遍,总算是跑通了。如果下次领导又要换到mysql怎么办?
这里我们不难发现,这不是一个良好的设计,组件之间高度耦合,可扩展性较差,它违背了DIP原则(高层模块定义了接口,将不再直接依赖于低层模块,低层模块负责实现高层模块定义的接口。这样,当有新的低层模块实现时,不需要修改高层模块的代码)。高层模块Order类不应该依赖于低层模块mysqlDal,oracelDal,两者应该依赖于抽象。
OK.这样设计的确很不好?那有没有什么好的方法解决这个问题?答案是肯定的,这里就需要引入前面介绍的"依赖注入"概念了。
依赖注入有三种实现方式:
1>构造函数注入:我们通过调用类的构造函数,将接口实现类通过构造函数变量传入
首先我们定义mysqlDal的抽象类型dataBaseTool,并声明一个add方法
 
public interface dataBaseTool { void Add(); }
然后在mysqlDal类中实现dataBaseTool接口
 
public class mysqlDal{ public void Add(){ system.out.println("入库成功!"); } }
接下来我们写一个order类
 
public class Order{ private dataBaseTool dbTool; //定义一个私有变量保存抽象 //构造注入 public Order(dataBaseTool dbTool){ this.dbTool = bdTool;//依赖传递 } public void Add(){ dbTool.Add(); } }
最后测试运行一下:
 
public class Test{ static void Main(string[] args) { mysqlDal dal= new mysqlDal();//在外部创建依赖对象 Order order = new Order(dal);//通过构造函数注入依赖 order.Add(); } }
最终结果:入库成功!
在上面的实现中,我们将mysqlDal对象的创建和绑定转移到Order类外部来实现,这样就解除了mysqlDal和Order类的耦合关系。当我们数据库换成Oracel数据库时,只需定义一个OracelDal类,然后外部重新绑定依赖,不需要修改Order类内部代码,则可实现Oracel数据库的操作。
2>属性注入:属性注入可以有选择地通过Setter方法完成调用类所需依赖的注入
首先定义一个mysqlDal类
 
public class mysqlDal{ public void Add(){ system.out.println("入库成功!"); } }
接下来定义一个Order类
 
public class Order{ private mysqlDal dal; public void setDal(mysqlDal dal) { this.dal= dal; } public void Add() { dal.Add(); } }
最后是测试代码:
 
public class Test{ static void Main(string[] args) { mysqlDal dal = new mysqlDal();//在外部创建依赖对象 Order order = new Order(); //调用属性Setter方法注入 order.setDal(dal); order.Add(); } }
最终结果依然是:入库成功!
和通过构造函数注入数据库类不同,在实例化订单类时,并未指定任何数据库类,而是在实例化后,在需要数据库出场时,才调用其setDal()方法注入数据库类。按照类似的方式,我们还可以分别为其他诸如Oracel方式提供注入的Setter方法,这样,订单就可以根据不同的需求,注入相应的数据库类了。
3>接口注入:将调用类所有依赖注入的方法抽取到一个接口中,调用类通过实现该接口提供相应的注入方法。然后依赖类继承并实现这个接口。
首先提供一个mysqlDal类
 
public class mysqlDal{ public void Add(){ system.out.println("入库成功!"); } }
然后定义一个接口
 
public interface DBCollection{ void injectDB(mysqlDal dal); }
接着定义Order类,并具体实现injectDB.通过接口方法注入
 
public class Order implements DBCollection{ private mysqlDal dal; //实现接口方法 public void injectDB(mysqlDal dal){ this.dal = dal; } public void Add(){ dal.Add(); } }
最后是测试类
 
public class Test{ public static void Main(string[] args) { mysqlDal dal = new mysqlDal();//在外部创建依赖对象 Order order = new Order(); order.injectDB(dal);//接口方法注入 order.Add(); } } }
最终结果:成功入库!
由于通过接口注入需要额外声明一个接口,增加了类的数目,而且它的效果和属性注入并无本质区别,因此我们不提倡采用这种方式。
这么多方案,接着又面临这新的问题:
对于大型项目来说,相互依赖的组件比较多。如果还用手动的方式,自己来创建和注入依赖的话,显然效率很低,而且往往还会出现不可控的场面。正因如此,IoC容器诞生了。IoC容器实际上是一个DI框架,它能简化我们的工作量。它包含以下几个功能:
- 动态创建、注入依赖对象。
- 管理对象生命周期。
- 映射依赖关系。
接着有新的解决方案:IOC容器这个第三方的容器,它帮助完成类的初始化与装配工作,让开发者从这些底层实现类的实例化、依赖关系装配等工作中脱离出来,专注于更有意义的业务逻辑开发工作。这无疑是一件令人向往的事情,Spring就是这样的一个容器,它通过配置文件或注解描述类和类之间的依赖关系,自动完成类的初始化和依赖注入的工作。下面是Spring配置文件的对以上实例进行配置的配置文件片断:
 
<?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <!--实现类实例化--> <bean id="order" class="mysqlDal"/> <bean id="Test" class="com.baobaotao.ioc.Test" p:geli-ref="order"/><!--通过order-ref建立依赖关系--> </beans>
通过new XmlBeanFactory(“beans.xml”)等方式即可启动容器。在容器启动时,Spring根据配置文件的描述信息,自动实例化Bean并完成依赖关系的装配,从容器中即可返回准备就绪的Bean实例,后续可直接使用之。
这里给一个spring实现依赖注入的实例,供参考
为何如此牛掰!Spring为什么会有这种“神奇”的力量,仅凭一个简单的配置文件,就能魔法般地实例化并装配好程序所用的Bean呢?这种“神奇”的力量归功于Java语言本身的类反射功能。 
参考文章: http://www.iteye.com/topic/1122835
http://www.cnblogs.com/liuhaorain/p/3747470.html


 
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号