面向对象设计(OOD)有助于我们开发出高性能、易扩展以及易复用的程序。当中。OOD有一个重要的思想那就是依赖倒置原则(DIP),并由此引申出IoC、DI以及Ioc容器等概念。

本文首先用实例阐述四个概念。而且给出Java版本号的演示样例代码。

依赖倒置原则(DIP)

依赖倒置是一种软件架构设计的原则,。依赖倒置原则,它转换了依赖,高层模块不依赖于低层模块的实现,而低层模块依赖于高层模块定义的接口。通俗的讲,就是高层模块定义接口,低层模块负责实现。

怎样理解呢?举例说明吧。

先看生活中的一个样例。

图1  ATM与银行卡

相信大部分取过钱的朋友都深有感触,仅仅要有一张卡,随便到哪一家银行的ATM都能取钱。在这个场景中。ATM相当于高层模块,而银行卡相当于低层模块。ATM定义了一个插口(接口),供全部的银行卡插入使用。也就是说,ATM不依赖于详细的哪种银行卡。

它仅仅需定义好银行卡的规格參数(接口)。全部实现了这样的规格參数的银行卡都能在ATM上使用。现实生活如此,软件开发更是如此。

依赖倒置(高层模块定义接口,低层模块负责实现)

在这个图中,我们发现高层模块定义了接口,将不再直接依赖于低层模块。低层模块负责实现高层模块定义的接口。

这样,当有新的低层模块实现时,不须要改动高层模块的代码。

由此。我们能够总结出使用DIP的长处:

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

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

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

控制反转(IoC)

DIP是一种 软件设计原则,它只告诉你两个模块之间应该怎样依赖,可是它并没有告诉怎样做。IoC则是一种 软件设计模式,它告诉你应该怎样做,来解除相互依赖模块的耦合。控制反转(IoC),它为相互依赖的组件提供抽象,将依赖(低层模块)对象的获得交给第三方(系统)来控制。即依赖对象不在被依赖模块的类中直接通过new来获取。

依赖注入(DI)

控制反转(IoC)一种重要的方式。就是将依赖对象的创建和绑定转移到被依赖对象类的外部来实现。主要实现有三种方式:构造函数注入;属性注入;接口注入。

以下以将订单存储到数据库为例,用Java演示样例来说明问题。

如果系统开发初期使用SqlServer。我们简历数据库操作类。

public class SqlServer {

public void add(){

System.out.println("Addorder to Sql Server!");

}

}

定义一个order类:

public class Order {

private SqlServer ss=newSqlServer();

public void add(){

ss.add();

}

}

測试一下:

public class Main {

public static void main(String[]args) {

Order o=new Order();

o.add();

}

}

输出:

到这里没问题,可是假设须要加入MySql的数据库呢?

这里须要建立操作MySql的数据库类:

public class MySql {

public void add(){

System.out.println("Add to mysqlServer");

}

}

订单类须要改动:

public class Order {

private MySql ss=new MySql();

public void add(){

ss.add();

}

}

測试输出:

假设还须要很多其它的数据库操作呢?这样下去还需改动代码。组件之间高度耦合,可扩展性较差,它违背了DIP原则。

依赖注入就是解决上述问题。

依赖注入主要有三种实现方式:构造函数注入;属性注入;接口注入。以下用演示样例进行说明。

1、        构造函数

构造函数函数注入。毫无疑问通过构造函数传递依赖。因此,构造函数的參数必定用来接收一个依赖对象。那么參数的类型是什么呢?详细依赖对象的类型?还是一个抽象类型?依据DIP原则,我们知道高层模块不应该依赖于低层模块。两者应该依赖于抽象。那么构造函数的參数应该是一个抽象类型。

定义接口:

public interface IDB {

publicvoid add();

}

然后在SqlServer中实现这个接口

publicclass SqlServer implements IDB{

@Override

public void add() {

// TODO Auto-generatedmethod stub

System.out.println("Addinto Sql Server");

}

}

改动order类,在构造函数中注入对象:

public class Order {

private IDB idb;

public Order(IDB idb){

this.idb=idb;

}

public void add(){

this.idb.add();

}

}

測试类:

public class Main {

publicstatic void main(String[] args) {

IDBidb=new SqlServer();

Ordero=new Order(idb);

o.add();

}

}

输出:

通过构造函数注入的方式我们将依赖对象SqlServer的创建和绑定转移到了Order类的外部来实现。这样就解除了SqlServe和Order类的讴歌关系。当我们须要换成MySql数据库的时候。仅仅须要又一次定义一个Mysql类实现IDB接口,在用到的地方新建。在Order的外部又一次绑定依赖。不须要改动Order的代码。

比如:

新建MySql类:

public class MySql implements IDB{

@Override

publicvoid add() {

//TODO Auto-generated method stub

System.out.println("Addinto MySql server!");

}

}

在Main函数中仅仅须要需改:

IDB idb=new MySql();

測试:

2、        属性注入

属性注入是通过属性来传递依赖。

因此,我们首先须要在依赖类Order中定义一个属性。

public class Order{

private IDB idb;//私有属性

public void add(){

this.idb.add();

}

public IDB getIdb() {

return idb;

}

public void setIdb(IDB idb) {

this.idb = idb;//接受依赖

}

}

接口类以及数据库訪问类同上。Main方法须要这么写:

public class Main{

public static void main(String[] args) {

IDB idb=new SqlServer();

Order o=new Order();

o.setIdb(idb);

o.add();

}

}

測试输出:

Add into SqlServer

假设须要扩展MySql数据库。仅仅须要在Main方法里改动为:

IDBidb=newMySql();

測试输出:

Add into MySql

3、        接口注入

相比构造函数注入和属性注入。接口注入显得有些复杂,使用也不常见。详细思路是先定义一个接口。包括一个设置依赖的方法。然后依赖类。继承并实现这个接口。

首先定义一个接口:

public interface IDependent {

publicvoid setDependency(IDB idb);

}

依赖类实现这个接口:

public class Order implements IDependent{

privateIDB idb;

@Override

publicvoid setDependency(IDB idb) {

//TODO Auto-generated method stub

this.idb=idb;

}

publicvoid add(){

this.idb.add();

}

}

还须要定义数据库訪问的接口:

public interface IDB {

publicvoid add();

}

详细数据库的实现须要实现这个接口:

public class SqlServer implements IDB{

@Override

publicvoid add() {

//TODO Auto-generated method stub

System.out.println("Addinto Sql Server");

}

}

測试类:

public class Main {

publicstatic void main(String[] args) {

Ordero=new Order();

IDBidb=new SqlServer();

o.setDependency(idb);

o.add();

}

}

測试输出:

Add into SqlServer

假设须要加入MySql的訪问须要定义MySql的实现:

public class MySql implements IDB{

@Override

publicvoid add() {

//TODO Auto-generated method stub

System.out.println("Addinto MySql");

}

}

在測试类仅仅须要作例如以下改动就可以:

IDBidb=newMySql();

測试输出:

Add into MySql。

IoC容器

简单的来讲就是抛弃手动的方式创建依赖对象传递给被依赖模块而选取DI框架替我们创建来减轻我们的工作量。对于大型项目来说,相互依赖的组件比較多。假设还用手动的方式。自己来创建和注入依赖的话,显然效率非常低,并且往往还会出现不可控的场面。正因如此。IoC容器诞生了。IoC容器实际上是一个DI框架,它能简化我们的工作量。它包括下面几个功能:

l  动态创建、注入依赖对象。

l  管理对象生命周期。

l  映射依赖关系。

Java中比較突出的就是Spring了。很多其它Spring的介绍将出如今后面的博文里。

不可不知的DIP、IoC、DI以及IoC容器的更多相关文章

  1. Atitit。如何实现dip, di ,ioc ,Service Locator的区别于联系

    Atitit.如何实现dip, di ,ioc  ,Service Locator的区别于联系 1. Dip原则又来自于松耦合思想方向1 2. 要实现dip原则,有以下俩个模式1 3. Ioc和di的 ...

  2. dip vs di vs ioc

    https://stackoverflow.com/questions/6766056/dip-vs-di-vs-ioc https://docs.microsoft.com/en-us/aspnet ...

  3. Spring4学习回顾之路02—IOC&DI

    IOC&DI介绍 ●IOC:(Inversion of Control) :控制反转(反向获取资源) 其思想是反转资源获取的方向.传统的资源上查找方式要求组件向容器发起请求查找资源,作为回应, ...

  4. 深入理解DIP、IoC、DI以及IoC容器

    摘要 面向对象设计(OOD)有助于我们开发出高性能.易扩展以及易复用的程序.其中,OOD有一个重要的思想那就是依赖倒置原则(DIP),并由此引申出IoC.DI以及Ioc容器等概念.通过本文我们将一起学 ...

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

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

  6. 深入理解DIP、IoC、DI以及IoC容器(转)

    深入理解DIP.IoC.DI以及IoC容器 摘要 面向对象设计(OOD)有助于我们开发出高性能.易扩展以及易复用的程序.其中,OOD有一个重要的思想那就是依赖倒置原则(DIP),并由此引申出IoC.D ...

  7. 【转】深入理解DIP、IoC、DI以及IoC容器

    原文链接:http://www.cnblogs.com/liuhaorain/p/3747470.html 前言 对于大部分小菜来说,当听到大牛们高谈DIP.IoC.DI以及IoC容器等名词时,有没有 ...

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

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

  9. DIP、IoC、DI以及IoC容器

    深入理解DIP.IoC.DI以及IoC容器 摘要 面向对象设计(OOD)有助于我们开发出高性能.易扩展以及易复用的程序.其中,OOD有一个重要的思想那就是依赖倒置原则(DIP),并由此引申出IoC.D ...

随机推荐

  1. 开源数据库连接池之Tomcat内置连接池

    本篇介绍几种开源数据库连接池,同时重点讲述如何使用Tomcat服务器内置的数据库连接池. 之前的博客已经重点讲述了使用数据库连接池的好处,即是将多次创建连接转变为一次创建而使用长连接模式.这样能减少数 ...

  2. QSplashScreen无法背景透明的解决办法

    setWindowFlags(Qt::WindowStaysOnTopHint | Qt::SplashScreen | Qt::FramelessWindowHint); setAttribute( ...

  3. Qt之日志输出文件

    在Qt开发过程当中经常使用qDebug等一些输出来调试程序,但是到了正式发布的时候,都会被注释或者删除,采用日志输出来代替.     做过项目的童鞋可能都使用过日志功能,以便有异常错误能够快速跟踪.定 ...

  4. android应用框架搭建------BaseActivity

    网上有很多介绍BaseActivity的博文,多数是从应用的角度去描述的. 这里,我所介绍的BaseActivity不同,主要从框架搭建的角度去介绍BaseActivity的使用. 先看代码: /** ...

  5. 基于redis的cas集群配置(转)

    1.cas ticket统一存储 做cas集群首先需要将ticket拿出来,做统一存储,以便每个节点访问到的数据一致.官方提供基于memcached的方案,由于项目需要,需要做计入redis,根据官方 ...

  6. 基于visual Studio2013解决面试题之0501上台阶

     题目

  7. 《火球——UML大战需求分析》(第2章 耗尽脑汁的需求分析工作)——2.1 需求分析面面观

    说明: <火球——UML大战需求分析>是我撰写的一本关于需求分析及UML方面的书,我将会在CSDN上为大家分享前面几章的内容,总字数在几万以上,图片有数十张.欢迎你按文章的序号顺序阅读,谢 ...

  8. LintCode 二叉树的层次遍历 II

    中等 二叉树的层次遍历 II 查看执行结果 42% 通过 给出一棵二叉树,返回其节点值从底向上的层次序遍历(按从叶节点所在层到根节点所在的层遍历,然后逐层从左往右遍历) 您在真实的面试中是否遇到过这个 ...

  9. wpf集成unity

    定义一个帮助类 实际上就是为了设置以下这两种属性 安全性信任和从html中可见的属性  即: 在html的javaScript中可用 window.external.方法名来调用C#方法    [Pe ...

  10. Swift - UIView的常用属性和常用方法总结

    1,UIView常用的一些属性如下: frame:相对父视图的坐标和大小(x,y,w,h) bounds:相对自身的坐标和大小,所以bounds的x和y永远为0(0,0,w,h) center:相对父 ...