1、概述

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

2、意图

面向过程的开发,上层调用下层,上层依赖于下层,当下层剧烈变动时上层也要跟着变动,这就会导致模块的复用性降低而且大大提高了开发的成本。
 
面向对象的开发很好的解决了这个问题,一般情况下抽象的变化概率很小,让用户程序依赖于抽象,实现的细节也依赖于抽象。即使实现细节不断变动,只要抽象不变,客户程序就不需要变化。这大大降低了客户程序与实现细节的耦合度。
 

3、正文

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

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

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

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

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

举个生活中的小例子:

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

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

转换下概念,也就是:

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

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

再来举个例子:

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

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

由此可见DIP的优点:

系统更柔韧:可以修改一部分代码而不影响其他模块。

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

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

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

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

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

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

MysqlHelper.java(数据库操作类)

 package com.lcw.dip.test;

 public class MysqlHelper {

     public void add(){
System.out.println("增加订单..");
} public void delete(){
System.out.println("删除订单..");
} public void update(){
System.out.println("修改订单..");
} public void find(){
System.out.println("查询订单..");
}
}

Order.java(业务逻辑类)

 package com.lcw.dip.test;

 public class Order {
private MysqlHelper helper = new MysqlHelper(); public void addOrder() {
this.helper.add();
} public void delOrder(){
this.helper.delete();
} public void updateOrder(){
this.helper.update();
} public void FindOrder(){
this.helper.find();
}
}

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(依赖注入),将创建对象的权利交给外部(第三方)实现,然后再传递给需要调用对象的模块,也就是高层模块。

传递注入的方式有三种:

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

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

3、接口注入:因为具有代码侵入性,一般很少用,前2种居多

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

DbHelper.java

 package com.lcw.dip.test;

 public class DbHelper {

     public void add(){
System.out.println("增加订单..");
} public void delete(){
System.out.println("删除订单..");
} public void update(){
System.out.println("修改订单..");
} public void find(){
System.out.println("查询订单..");
}
}

Order.java

 package com.lcw.dip.test;

 public class Order {
//private MysqlHelper helper = new MysqlHelper();
private DbHelper helper;
public Order(DbHelper helper){//提供构造方法,注入属性
this.helper=helper;
} public void addOrder() {
this.helper.add();
} public void delOrder(){
this.helper.delete();
} public void updateOrder(){
this.helper.update();
} public void FindOrder(){
this.helper.find();
}
}

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();
DbHelper helper=new DbHelper();
Order order=new Order(helper);//注入DbHelper对象
order.addOrder();
} }

效果依旧,这样就很方便我们下次修改了,比如我们要换成Access数据库,那么这次我们只需要去修改数据库操纵类DbHelper就可以了,就不必要去动Order类了。

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

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

Order.java

 package com.lcw.dip.test;

 public class Order {
// private MysqlHelper helper = new MysqlHelper();
// private DbHelper helper;
// public Order(DbHelper helper){//提供构造方法,注入属性
// this.helper=helper;
// } private DbHelper helper;
public void setHelper(DbHelper helper) {
this.helper = helper;
} public void addOrder() {
this.helper.add();
} public void delOrder(){
this.helper.delete();
} public void updateOrder(){
this.helper.update();
} public void FindOrder(){
this.helper.find();
}
}

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();
// DbHelper helper=new DbHelper();
// Order order=new Order(helper);//注入DbHelper对象
DbHelper helper=new DbHelper();
Order order=new Order();
order.setHelper(helper);
order.addOrder();
} }

效果依旧:

最后来说下关于Ioc容器:

在上面的例子中,我们都是通过手动的方式来创建依赖对象,然后在手动传递给被依赖模块(高层),但对于大型的项目来说,各个组件之间的依赖关系式非常复杂的,如果我们还是用手动来创建依赖对象并且手动注入是个相当繁杂的一个工作,而且还容易出错,甚至出现不可控状态。

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

Ioc容器有很多比如:PicoContainer,JBoss Microcontainer,Soto,Spring等。

总结一下:

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

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

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

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

对依赖倒置原则(DIP)及Ioc、DI、Ioc容器的一些理解的更多相关文章

  1. 依赖倒置原则DIP&控制反转IOC&依赖注入DI

    依赖倒置原则DIP是软件设计里一个重要的设计思想,它规定上层不依赖下层而是共同依赖抽象接口,通常可以是上层提供接口,然后下层实现接口,上下层之间通过接口完全透明交互.这样的好处,上层不会因依赖的下层修 ...

  2. C#软件设计——小话设计模式原则之:依赖倒置原则DIP

    前言:很久之前就想动笔总结下关于软件设计的一些原则,或者说是设计模式的一些原则,奈何被各种bootstrap组件所吸引,一直抽不开身.群里面有朋友问博主是否改行做前端了,呵呵,其实博主是想做“全战”, ...

  3. 7.12 其他面向对象设计原则3: 依赖倒置原则DIP

    其他面向对象设计原则3: 依赖倒置原则DIP  The Dependency Inversion Principle7.1 依赖倒置原则DIP The Dependency Inversion Pr ...

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

    所谓依赖倒置原则(Dependence Inversion Principle)就是要依赖于抽象,不要依赖于具体.简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合 ...

  5. 依赖倒置原则(DIP)

    什么是依赖倒置呢?简单地讲就是将依赖关系倒置为依赖接口,具体概念如下: 1.上层模块不应该依赖于下层模块,它们共同依赖于一个抽象(父类不能依赖子类,它们都要依赖于抽象类) 2.抽象不能依赖于具体,具体 ...

  6. 设计模式学习--面向对象的5条设计原则之依赖倒置原则--DIP

    一.DIP简介(DIP--Dependency Inversion Principle): 1.高层模块不应该依赖于低层模块,二者都应该依赖于抽象.2.抽象不应该依赖于细节,细节应该依赖于抽象.   ...

  7. 深入理解JavaScript系列(22):S.O.L.I.D五大原则之依赖倒置原则DIP

    前言 本章我们要讲解的是S.O.L.I.D五大原则JavaScript语言实现的第5篇,依赖倒置原则LSP(The Dependency Inversion Principle ). 英文原文:htt ...

  8. IOS设计模式的六大设计原则之依赖倒置原则(DIP,Dependence Inversion Principle)

    定义 高层模块不应该依赖于低层模块,二者都应该依赖于抽象:抽象不应该依赖细节:细节应该依赖抽象. 定义解读 依赖倒置原则在程序编码中经常运用,其核心思想就是面向接口编程,高层模块不应该依赖低层模块(原 ...

  9. 依赖倒置原则DIP(面向接口编程—OOD)

    含义: 1.高层模块不应该依赖底层模块,两者都应该依赖其抽象. 2.抽象不应该依赖细节. 3.细节应该依赖抽象. 底层模块:不可分割的原子逻辑. 高层模块: 原子逻辑的再组装. 抽象:接口或者抽象类, ...

随机推荐

  1. 重装Windows系统后,Linux系统启动引导失败

    说明:我的电脑是在装了Windows系统后,装的Linux系统,所以启动引导是Linux干的活.可是,今天重装了Windows系统,启动引导就换成了Windows的,Windows的启动引导把Linu ...

  2. 地理围栏算法解析(Geo-fencing)

    地理围栏算法解析 http://www.cnblogs.com/LBSer/p/4471742.html 地理围栏(Geo-fencing)是LBS的一种应用,就是用一个虚拟的栅栏围出一个虚拟地理边界 ...

  3. mac下缺乏make wget怎么办?

    1.下载command Line tools for Xcode http://www.mkyong.com/mac/how-to-install-gcc-compiler-on-mac-os-x/ ...

  4. [jQuery学习系列三 ]3-JQuery学习二-字典操作

    前言:如果看过了第一篇和第二篇, 相信大家会对jQuery有个初步的认识了, 对于jQuery的选择器和数组的操作都已经很熟悉了, 这一篇就单独罗列jQuery中字典的操作相关的内容. 1. 数组中添 ...

  5. Atitit.dwr3 不能显示错误详细信息的解决方案,控件显示错误详细信息的解决方案 java .net php

    Atitit.dwr3 不能显示错误详细信息的解决方案,控件显示错误详细信息的解决方案 java .net php 1. Keyword/subtitle 1 2. 使用dwr3的异常convert处 ...

  6. MultiTouch————多点触控,伸缩图片,变换图片位置

    前言:当今的手机都支持多点触控功能(可以进行图片伸缩,变换位置),但是我们程序员要怎样结合硬件去实现这个功能呢? 跟随我一起,来学习这个功能 国际惯例:先上DEMO免费下载地址:http://down ...

  7. Bootstrap中水平排列的表单form-inline

    <html> <head> <title>初识Bootstrap</title> <meta charset="utf-8"& ...

  8. Leetcode 234 Palindrome Linked List 链表

    判断链表是否是回文. 我直接将链表的一半进行倒置,然后将两半的链表进行比较 /** * Definition for singly-linked list. * struct ListNode { * ...

  9. python一套完整的事务操作

    #coding=utf-8 import sys import MySQLdb class TransferMoney(object): def __init__(self,conn): self.c ...

  10. echart.js的使用与API

    ---恢复内容开始--- echart.js的使用与API 1.echart.js的使用: 第一步:在head标签或body下创建一个script标签去引用echart.js,(该文件可以在echar ...