miketwais

work up

面向对象设计(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("入库成功!");
}

}
View Code

order处理类,负责订单的逻辑处理。由于订单要入库,需要依赖于数据库的操作。因此在Order类中,我们需要定义mysqlDal类的变量并初始化。

public class Order
{
        private mysqlDal dal = new mysqlDal ();//添加一个私有变量保存数据库操作的对象
 
         public void Add()
       {
           dal.Add();
       }
}
View Code

下面我们测试一下:

public  class Program
    {
        static void Main(string[] args)
        {
            Order order = new Order();
            order.Add();
        }
    }
View Code

运行结果是:入库成功!

某一天接到通知,全线切换到oracle数据库,好吧,我们坑吱坑吱的把上面的代码重新改一遍,总算是跑通了。如果下次领导又要换到mysql怎么办?

这里我们不难发现,这不是一个良好的设计,组件之间高度耦合,可扩展性较差,它违背了DIP原则(高层模块定义了接口,将不再直接依赖于低层模块,低层模块负责实现高层模块定义的接口。这样,当有新的低层模块实现时,不需要修改高层模块的代码)。高层模块Order类不应该依赖于低层模块mysqlDal,oracelDal,两者应该依赖于抽象。

OK.这样设计的确很不好?那有没有什么好的方法解决这个问题?答案是肯定的,这里就需要引入前面介绍的"依赖注入"概念了。

依赖注入有三种实现方式:

1>构造函数注入:我们通过调用类的构造函数,将接口实现类通过构造函数变量传入

 首先我们定义mysqlDal的抽象类型dataBaseTool,并声明一个add方法

public interface dataBaseTool
{
        void Add();
}
View Code

然后在mysqlDal类中实现dataBaseTool接口

public class mysqlDal{
public void Add(){
  system.out.println("入库成功!");
}
}
View Code

接下来我们写一个order类

public class Order{
private dataBaseTool dbTool; //定义一个私有变量保存抽象
//构造注入
public Order(dataBaseTool dbTool){
  this.dbTool = bdTool;//依赖传递
}
public void Add(){
 dbTool.Add();
}

}
View Code

最后测试运行一下:

public class Test{
static void Main(string[] args)
        {
            mysqlDal dal= new mysqlDal();//在外部创建依赖对象
            Order order = new Order(dal);//通过构造函数注入依赖
            order.Add();
        }
}
View Code

最终结果:入库成功!

在上面的实现中,我们将mysqlDal对象的创建和绑定转移到Order类外部来实现,这样就解除了mysqlDal和Order类的耦合关系。当我们数据库换成Oracel数据库时,只需定义一个OracelDal类,然后外部重新绑定依赖,不需要修改Order类内部代码,则可实现Oracel数据库的操作。

 

2>属性注入:属性注入可以有选择地通过Setter方法完成调用类所需依赖的注入

首先定义一个mysqlDal类

public class mysqlDal{
  public void Add(){
    system.out.println("入库成功!");
  }
}
View Code

接下来定义一个Order类

public class Order{
private mysqlDal dal;
public void setDal(mysqlDal dal) {    
        this.dal= dal;  
    }  
    public void Add() {  
        dal.Add();  
    }  
}
View Code

最后是测试代码:

public class Test{
static void Main(string[] args)
        {
            mysqlDal dal = new mysqlDal();//在外部创建依赖对象
            Order order = new Order();
            //调用属性Setter方法注入 
            order.setDal(dal);
            order.Add();
        }
}            
View Code

最终结果依然是:入库成功!

和通过构造函数注入数据库类不同,在实例化订单类时,并未指定任何数据库类,而是在实例化后,在需要数据库出场时,才调用其setDal()方法注入数据库类。按照类似的方式,我们还可以分别为其他诸如Oracel方式提供注入的Setter方法,这样,订单就可以根据不同的需求,注入相应的数据库类了。 

 

3>接口注入:将调用类所有依赖注入的方法抽取到一个接口中,调用类通过实现该接口提供相应的注入方法。然后依赖类继承并实现这个接口。

首先提供一个mysqlDal类

public class mysqlDal{
public void Add(){
   system.out.println("入库成功!");
}
}
View Code

然后定义一个接口

public interface DBCollection{
 void injectDB(mysqlDal dal);
}
View Code

接着定义Order类,并具体实现injectDB.通过接口方法注入

public class Order implements DBCollection{
private mysqlDal dal;
//实现接口方法 
public void injectDB(mysqlDal dal){
  this.dal = dal;
}
public void Add(){
 dal.Add();
}
}
View Code

最后是测试类

public class Test{
public static void Main(string[] args)
        {
            mysqlDal dal = new mysqlDal();//在外部创建依赖对象
            Order order = new Order();
            order.injectDB(dal);//接口方法注入
            order.Add();
        }
    }
}
View Code

 最终结果:成功入库!

由于通过接口注入需要额外声明一个接口,增加了类的数目,而且它的效果和属性注入并无本质区别,因此我们不提倡采用这种方式。

 

这么多方案,接着又面临这新的问题:

对于大型项目来说,相互依赖的组件比较多。如果还用手动的方式,自己来创建和注入依赖的话,显然效率很低,而且往往还会出现不可控的场面。正因如此,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> 
View Code

通过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

posted @ 2017-07-21 17:34  MasonZhang  阅读(343)  评论(0)    收藏  举报