对依赖倒置原则(DIP)及Ioc、DI、Ioc容器的一些理解

一、概述

所谓依赖倒置原则(Dependence Inversion Principle)就是要依赖于抽象,不要依赖于具体。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就下降了客户与实现模块间的耦合,并由此引伸出IoC、DI以及Ioc容器等概念。java

 

二、意图

面向过程的开发,上层调用下层,上层依赖于下层,当下层剧烈变更时上层也要跟着变更,这就会致使模块的复用性下降并且大大提升了开发的成本。
 
面向对象的开发很好的解决了这个问题,通常状况下抽象的变化几率很小,让用户程序依赖于抽象,实现的细节也依赖于抽象。即便实现细节不断变更,只要抽象不变,客户程序就不须要变化。这大大下降了客户程序与实现细节的耦合度。
 

三、正文

依赖倒置原则(DIP):一种软件架构设计的原则(抽象概念)。sql

控制反转(IoC):一种反转流、依赖和接口的方式(DIP的具体实现方式)。数据库

依赖注入(DI):IoC的一种实现方式,用来反转依赖(IoC的具体实现方式)。编程

IoC容器:依赖注入的框架,用来映射依赖,管理对象建立和生存周期(DI框架)。设计模式

 

一个一个来讲吧,首先先来了解下"依赖倒置原则(DIP)":架构

举个生活中的小例子:框架

取过钱的朋友都知道,只要咱们手上有一张银行卡,咱们就能够到各个银行的ATM机上去取款,在这个场景中ATM机器属于高层模块,咱们手上的银行卡属于底层模块测试

在ATM机上提供了一个卡槽插口(接口),供各类银行卡插入使用,在这里ATM机不依赖于具体的哪一种银行卡,它只规定了银行卡的规格,只要咱们手上的银行卡知足这个规格参数,咱们就可使用它。this

转换下概念,也就是:spa

高层模块不依赖于底层模块,而底层模块依赖于高层模块的接口(高层模块定义接口,底层模块负责实现)。

 

高层模块(接口):抽象  底层模块(实现接口):实现  ==>二者应该依赖于抽象,抽象(高层)不依赖实现(底层),实现(底层)依赖于抽象(高层)。

 

再来举个例子:

一、若是依赖不倒置将会出现:高层模块依赖于底层模块,也就是说底层变成了抽象,高层须要实现抽象出来的全部接口,一旦底层出现新的模块,则就须要去修改高层的模块,破坏了开放-封闭原则。

二、若是依赖倒置将会出现:底层模块依赖于高层模块,也就是说高层变成了抽象,底层只须要去实现高层的接口就行,一旦底层出现新的模块,则高层模块就不须要去修改(定义抽象接口不变)。

 

因而可知DIP的优势:

系统更柔韧:能够修改一部分代码而不影响其余模块。

系统更健壮:能够修改一部分代码而不会让系统崩溃。

系统更高效:组件松耦合,且可复用,提升开发效率。

 

 

接下来讲下"控制反转(Ioc)":

DIP是一种软件设计原则,是告诉咱们模块之间应该是怎样的一种关系,那Ioc就是具体的一种软件设计模式,告诉咱们应该如何去作,才能作到程序间的解耦。

Ioc(控制反转)为高、低层模块之间提供了抽象,也就是第三方系统,也就是依赖对象(底层对象)不在依赖的模块中(高层模块)中直接建立对象,而是把建立对象的权利交给第三次Ioc容器来建立,而后再把对象交给依赖模块(联想刚刚取钱的例子,ATM机器是高层模块,它自身并无决定要插入哪一个银行的银行卡,好比建行,农行,要插入什么卡的决定权在于咱们(也就是第三方),咱们插入什么行的卡,它就给咱们什么银行的服务)。

 

来个具体代码感觉下Ioc的好处:(订单系统,底层操纵类是基于Mysql数据库的)

MysqlHelper.java(数据库操做类)

 1 package com.lcw.dip.test;
 2 
 3 public class MysqlHelper {
 4     
 5     public void add(){
 6         System.out.println("增长订单..");
 7     }
 8 
 9     public void delete(){
10         System.out.println("删除订单..");
11     }
12     
13     public void update(){
14         System.out.println("修改订单..");
15     }
16     
17     public void find(){
18         System.out.println("查询订单..");
19     }
20 }

 

Order.java(业务逻辑类)

 1 package com.lcw.dip.test;
 2 
 3 public class Order {
 4     private MysqlHelper helper = new MysqlHelper();
 5 
 6     public void addOrder() {
 7         this.helper.add();
 8     }
 9     
10     public void delOrder(){
11         this.helper.delete();
12     }
13     
14     public void updateOrder(){
15         this.helper.update();
16     }
17     
18     public void FindOrder(){
19         this.helper.find();
20     }
21 }

 

DipTest.java(测试类)

package com.lcw.dip.test;

/**
 *DIP(Dependence Inversion Principle)依赖倒置原则
 * @author Balla_兔子
 *
 */
public class DipTest {
    public static void main(String[] args) {
        Order order=new Order();
        order.addOrder();
    }

}

看下操做效果:

Perfect,完美!!

但若是如今忽然业务需求要改换成Access数据库,这时改怎么办呢?

传统的作法,咱们须要再去编写一个关于Access的数据库操纵类,而后修改下Order类里的代码,把实例化对象修改为Access类(new Access())。

那要是过几天又要改为Oracle数据库呢?

。。。。。反反复复,周而复始,烦!

 

有没有什么办法能够解决这个繁琐的问题呢?答案是必须有!否则我就不用打这么多字了~~

接下来依赖注入(DI)就派上用场了:

依赖注入是实现Ioc的一种重要方式,将依赖的对象的建立权交给外部(第三方)来处理,而不是在自身new出一个实例。

例如上面的添加订单例子,咱们在建立数据库操纵对象的时候是在Order类中直接new出,这样有个很很差的地方就是,一旦数据库变更,则咱们还要去修改Order类,很显然这是不可取的,违反了开放-封闭原则 。

那咱们应该怎么作呢?答案很明显就是利用DI(依赖注入),将建立对象的权利交给外部(第三方)实现,而后再传递给须要调用对象的模块,也就是高层模块。

传递注入的方式有三种:

一、构造注入:顾名思义利用构造方法注入

二、setter方法注入:在须要注入的类里提供一个setter方法

三、接口注入:由于具备代码侵入性,通常不多用,前2种居多

 

说了这么多,上代码直接看实例吧

DbHelper.java

 1 package com.lcw.dip.test;
 2 
 3 public class DbHelper {
 4     
 5     public void add(){
 6         System.out.println("增长订单..");
 7     }
 8 
 9     public void delete(){
10         System.out.println("删除订单..");
11     }
12     
13     public void update(){
14         System.out.println("修改订单..");
15     }
16     
17     public void find(){
18         System.out.println("查询订单..");
19     }
20 }

 

Order.java

 1 package com.lcw.dip.test;
 2 
 3 public class Order {
 4     //private MysqlHelper helper = new MysqlHelper();
 5     private DbHelper helper;
 6     public Order(DbHelper helper){//提供构造方法,注入属性
 7         this.helper=helper;
 8     }
 9 
10     public void addOrder() {
11         this.helper.add();
12     }
13     
14     public void delOrder(){
15         this.helper.delete();
16     }
17     
18     public void updateOrder(){
19         this.helper.update();
20     }
21     
22     public void FindOrder(){
23         this.helper.find();
24     }
25 }

 

DipTest.java

 1 package com.lcw.dip.test;
 2 
 3 /**
 4  *DIP(Dependence Inversion Principle)依赖倒置原则
 5  * @author Balla_兔子
 6  *
 7  */
 8 public class DipTest {
 9     public static void main(String[] args) {
10         //Order order=new Order();
11         DbHelper helper=new DbHelper();
12         Order order=new Order(helper);//注入DbHelper对象
13         order.addOrder();
14     }
15 
16 }

 

效果依旧,这样就很方便咱们下次修改了,好比咱们要换成Access数据库,那么此次咱们只须要去修改数据库操纵类DbHelper就能够了,就没必要要去动Order类了。

再来看下利用setter方法的注入:

 

DbHelper.java 数据库操做底层类不变

 

Order.java

 1 package com.lcw.dip.test;
 2 
 3 public class Order {
 4 //    private MysqlHelper helper = new MysqlHelper();
 5 //    private DbHelper helper;
 6 //    public Order(DbHelper helper){//提供构造方法,注入属性
 7 //        this.helper=helper;
 8 //    }
 9     
10     private DbHelper helper;
11     public void setHelper(DbHelper helper) {
12         this.helper = helper;
13     }
14 
15     public void addOrder() {
16         this.helper.add();
17     }
18     
19     public void delOrder(){
20         this.helper.delete();
21     }
22     
23     public void updateOrder(){
24         this.helper.update();
25     }
26     
27     public void FindOrder(){
28         this.helper.find();
29     }
30 }

 

DipTest.java

 1 package com.lcw.dip.test;
 2 
 3 /**
 4  *DIP(Dependence Inversion Principle)依赖倒置原则
 5  * @author Balla_兔子
 6  *
 7  */
 8 public class DipTest {
 9     public static void main(String[] args) {
10 //        Order order=new Order();
11 //        DbHelper helper=new DbHelper();
12 //        Order order=new Order(helper);//注入DbHelper对象
13         DbHelper helper=new DbHelper();
14         Order order=new Order();
15         order.setHelper(helper);
16         order.addOrder();
17     }
18 
19 }

效果依旧:

 

最后来讲下关于Ioc容器:

在上面的例子中,咱们都是经过手动的方式来建立依赖对象,而后在手动传递给被依赖模块(高层),但对于大型的项目来讲,各个组件之间的依赖关系式很是复杂的,若是咱们仍是用手动来建立依赖对象而且手动注入是个至关繁杂的一个工做,并且还容易出错,甚至出现不可控状态。

所以Ioc容器就这样诞生了,也就是DI的一个框架,用来简化咱们的操做,Ioc容器能够作到动态建立、注入对象,对象的生命周期管理,映射依赖关系等。

Ioc容器有不少好比:PicoContainer,JBoss Microcontainer,Soto,Spring等。

 

总结一下:

DIP是软件设计的一种思想,IoC则是基于DIP衍生出的一种软件设计模式。

DI是IoC的具体实现方式之一,使用最为普遍。

IoC容器是DI注入的框架,它管理着依赖项的生命周期以及映射关系。

 

 

做者:Balla_兔子
出处:http://www.cnblogs.com/lichenwei/本文版权归做者和博客园共有,欢迎转载,但未经做者赞成必须保留此段声明,且在文章页面明显位置给出原文连接。正在看本人博客的这位童鞋,我看你气度不凡,谈吐间隐隐有王者之气,往后必有一番做为!旁边有“推荐”二字,你就顺手把它点了吧,相得准,我分文不收;相不许,你也好回来找我!

相关文章
相关标签/搜索