控制反转IOC, 全称 “Inversion of Control”。依赖注入DI, 全称 “Dependency Injection”。

一个简单的场景: 

当一个类的实例需要另一个类的实例协助时,在传统的程序设计过程中,通常有调用者来创建被调用者的实例, 并使用。

面向的问题:

软件开发中,为了降低模块间、类间的耦合度,提倡基于接口的开发,那么在实现中必须面临最终是有“谁”提供实体类的问题。(将各层的对象以松耦合的方式组织起来,各层对象的调用面向接口。)

理解将组件的配置和使用分离: 

如果觉得这句话比较抽象, 可以将"组件"理解为"对象"(底层组件),那么相应的“组件的配置”就可以理解成为“对象的初始化”, 再来理解这句话,就是将对象的创建和使用分离. 其优点很冥想, 将对象的创建延迟到部署阶段(这句话也可能不太好理解), 就是说对象的创建全部依赖于统一的配置(声明), 这样我们就可以通过修改配置动态地把我们期望使用的类替换成我们打算使用的类,而不修改任何使用对象的原有代码. 原则上,我们需要把对象的装配(配置/声明)和业务代码(使用)分离开来. 

软件系统中的依赖(耦合):

在采用面向对象设计的软件系统中,万物皆对象,所有的对象通过彼此的合作,完成整个系统的工作.就好比下面的齿轮,每个齿轮转动才能保证整个齿轮系统的运转.但是这样的设计就意味着强依赖,强耦合. 如果某个齿轮出现问题了(发生改变), 整个齿轮系统可能就会瘫痪, 这显然是不能接受的. 

什么是控制反转(IOC)? 

耦合关系不仅会出现在对象与对象之间, 也会出现在软件系统的各个模块之间,以及软件系统和硬件系统之间.如何降低系统之间, 模块之间和对象之间的耦合度, 是软件工程永远追求的目标. 

为了解决对象之间的耦合度过高的问题, 软件专家Michael Mattson提出了IOC理论, 用来实现对象之间的"解耦". 目前这个理论已经被成熟的应用到项目开发中, 衍生出了各式各样的IOC框架产品.

IOC理论提出的大致观点是这样的: 借助于"第三方"实现具有依赖关系的对象之间的解耦, 如下图:

由于引进了中间位置的"第三方",这里成为IOC容器(Container), 使得A, B, C, D这4个对象没有了耦合关系, 齿轮之间的传动全部依靠"第三方"了, 换句话说, 全部对象的控制权全部上缴给"第三方"IOC容器,

所以, IOC容器成了整个系统的关键核心, 它将对象之间的依赖关系降低到了最低程度.

为了在详细说明其中的差别, 我们来对比下: 在没有引入IOC容器之前, 第一张图中, 对象A依赖于对象B, 那么A在初始化或者运行到某一点的时候, A自己必须主动创建对象B或者直接使用对象B. 而无论是创建还是使用对象B, 控制权在A自己手上.

软件开发引入IOC容器之后, 这种情况就完全不同了, 第二章图中, 由于IOC容器的加入, 对象A和B之间失去了直接联系, 所以, 当对象A运行到需要对象B的时候, IOC容器主动创建一个对象B, 并将其注入到对象A需要的地方.

通过前后的对比, 也就不难看出: 对象A获得依赖对象B的过程, 由主动行为变为被动行为, 控制权发生转换, 这就是"控制反转"的由来.

什么是依赖注入(DI)?

2004年, Martin Fowler,在其著名的文章《Inversion of Control Containers and the Dependency Injection pattern》探讨了同一个问题, 既然IOC是控制反转, 那么到底"那些方面的控制需要被反转呢?' 经过详细地分析和论证后,

他得出了答案: “获得依赖对象的过程需要反转”. 控制被反转之后, 获得依赖对象的过程由自身管理变为了由IOC容器主动注入. 于是,他给"控制反转"取了一个更适合的名称叫做"依赖注入 Dependency Injection"。

他的这个答案, 实际上给出了实现IOC的方法: 依赖注入所谓依赖注入, 就是由IOC容器在运行期间, 动态地将某种依赖关系注入到对象之中. 

也就是说: 采用依赖注入的方式,创建被调用者的实例的工作不再由调用者完成,而是由IOC容器来完成,这就是“控制反转”的意思,然后,将其注入调用者,因此也称为 “依赖注入”。

我们也可以知晓, 控制反转(IOC) 和 依赖注入(DI) 是从不同角度对同一件事务的描述. 就是通过IOC容器, 利用注入依赖关系的方式, 实现对象之间的解耦.

Martin Fowler在文中将具体依赖注入划分为三种形式,即构造器注入、属性(设置)注入和接口注入。

习惯将其划分为一种(类型)匹配和三种注入:

  • 类型匹配(Type Matching):虽然我们通过接口(或者抽象类)来进行服务调用,但是服务本身还是实现在某个具体的服务类型中,这就需要某个类型注册机制来解决服务接口和服务类型之间的匹配关系;
  • 构造器注入(Constructor Injection):IoC容器会智能地选择选择和调用适合的构造函数以创建依赖的对象。如果被选择的构造函数具有相应的参数,IoC容器在调用构造函数之前解析注册的依赖关系并自行获得相应参数对象;
  • 属性注入(Property Injection):如果需要使用到被依赖对象的某个属性,在被依赖对象被创建之后,IoC容器会自动初始化该属性;
  • 方法注入(Method Injection):如果被依赖对象需要调用某个方法进行相应的初始化,在该对象创建之后,IoC容器会自动调用该方法

创建一个控制台程序,定义如下几个接口(IA、IB、IC和ID)和它们的实现类(A、B、C、D)。在类型A中定义了三个属性B、C和D,其参数类型分别为IB、IC和ID。

其中,

属性B作为构函数的参数,认为它会以构造器注入的方式被初始化 (??);

属性C应用了DependencyAttribute特性,意味着这是一个需要以属性注入方式被初始化的依赖属性;

属性D则通过方法Initialize初始化,该方法上应用了特性InjectionMethodAttribute, 意味着这是一个方法注入,在A对象被Ioc容器创建的时候,D会被自动调用。

Microsoft有一个轻量级的IoC框架Unity, 支持构造器注入,属性注入,方法注入。对于C#语言,由于语法元素上本身较其他语言丰富许多,如何实施注入还有些技巧和特色之处。

下面介绍如下:

测试类:

namespace UnityDemo
{
public interface IA { }
public interface IB { }
public interface IC { }
public interface ID { } public class A : IA
{
public IB B { get; set; } [Dependency]
public IC C { get; set; }
public ID D { get; set; } public A(IB b)
{
this.B = b;
        // I am A
} [InjectionMethod]
public void Initialize(ID d)
{
this.D = d;
}
} public class B : IB { // I am B }
public class C : IC { // I am C }
public class D : ID { // I am D }
}

配置注册:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/>
</configSections>
<unity>
<containers>
<container name="defaultContainer">
<register type="UnityDemo.IA, UnityDemo" mapTo="UnityDemo.A, UnityDemo"/>
<register type="UnityDemo.IB, UnityDemo" mapTo="UnityDemo.B, UnityDemo"/>
<register type="UnityDemo.IC, UnityDemo" mapTo="UnityDemo.C, UnityDemo"/>
<register type="UnityDemo.ID, UnityDemo" mapTo="UnityDemo.D, UnityDemo"/>
</container>
</containers>
</unity> <startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
</configuration>

Main方法中,创建一个IOC容器的UnityContainer对象,并加载配置信息对其初始化,然后调用它的泛型的Resolve()方法创建一个实现了泛型接口IA的对象。

最后将返回对象转换成类型A, 并逐一检验B,C和D属性是否为空,即初始化情况。

namespace UnityDemo
{
class Program
{
static void Main(string[] args)
{
var container = new UnityContainer();
var configuration = ConfigurationManager.GetSection(UnityConfigurationSection.SectionName) as UnityConfigurationSection;
configuration.Configure(container, "defaultContainer"); A a = container.Resolve<IA>() as A;
if (null != a)
{
Console.WriteLine("a.B==null? {0}", a.B == null ? "Yes" : "No");
Console.WriteLine("a.C==null? {0}", a.C == null ? "Yes" : "No");
Console.WriteLine("a.D==null? {0}", a.D == null ? "Yes" : "No");
} }
}
}

执行结果:

分别体现了接口注入构造器注入(属性B)属性注入(属性C)方法注入(属性D)

依赖注入的技术点

IOC中最基本的技术就是"反射 (Reflection)" 编程. 关于反射的概念, 通俗地讲代码运行阶段, 根据给出的信息动态的生成对象.

JACK D. @ NJ USA

控制反转(Ioc)和依赖注入(DI)的更多相关文章

  1. 控制反转IOC与依赖注入DI

    理解 IOC  http://www.cnblogs.com/zhangchenliang/archive/2013/01/08/2850970.html IOC 相关实例      的http:// ...

  2. iOS控制反转(IoC)与依赖注入(DI)的实现

    背景 最近接触了一段时间的SpringMVC,对其控制反转(IoC)和依赖注入(DI)印象深刻,此后便一直在思考如何使用OC语言较好的实现这两个功能.Java语言自带的注解特性为IoC和DI带来了极大 ...

  3. 轻松学,浅析依赖倒置(DIP)、控制反转(IOC)和依赖注入(DI) 依赖注入和控制反转的理解,写的太好了。

    轻松学,浅析依赖倒置(DIP).控制反转(IOC)和依赖注入(DI) 2017年07月13日 22:04:39 frank909 阅读数:14269更多 所属专栏: Java 反射基础知识与实战   ...

  4. 控制反转IOC与依赖注入DI【转】

    转自:http://my.oschina.net/1pei/blog/492601 一直对控制反转.依赖注入不太明白,看到这篇文章感觉有点懂了,介绍的很详细. 1. IoC理论的背景我们都知道,在采用 ...

  5. 【转载】浅析依赖倒置(DIP)、控制反转(IOC)和依赖注入(DI)

    原文地址 http://blog.csdn.net/briblue/article/details/75093382 写这篇文章的原因是这两天在编写关于 Dagger2 主题的博文时,花了大量的精力来 ...

  6. 控制反转IOC与依赖注入DI - 理论篇

    学无止境,精益求精 十年河东十年河西,莫欺少年穷 昨天是五一小长假归来上班的第一天,身体疲劳,毫无工作热情.于是就看看新闻,喝喝茶,荒废了一天 也就在昨天,康美同事张晶童鞋让我学习下IOC的理论及实现 ...

  7. 依赖倒置(DIP)、控制反转(IOC)和依赖注入(DI)

    原文: https://blog.csdn.net/briblue/article/details/75093382 写这篇文章的原因是这两天在编写关于 Dagger2 主题的博文时,花了大量的精力来 ...

  8. 20181123_控制反转(IOC)和依赖注入(DI)

    一.   控制反转和依赖注入: 控制反转的前提, 是依赖倒置原则, 系统架构时,高层模块不应该依赖于低层模块,二者通过抽象来依赖 (依赖抽象,而不是细节) 如果要想做到控制反转(IOC), 就必须要使 ...

  9. Spring框架学习笔记(1)——控制反转IOC与依赖注入DI

    Spring框架的主要作用,就是提供了一个容器,使用该容器就可以创建并管理对象.比如说Dao类等,又或者是具有多依赖关系的类(Student类中包含有Teacher类的成员变量) Spring有两个核 ...

随机推荐

  1. JavaScript toFixed function Not Rouding

    JavaScript库函数toFixed用来将给定的数字四舍五入为指定的小数位数,W3school上有详细的介绍.众所周知,在处理小数位四舍五入的时候存在两种方式:一种是逢五进一,如5.885保留两位 ...

  2. lucene join解决父子关系索引

    http://www.cnblogs.com/LBSer/p/4417074.html 1 背景 以商家(Poi)维度来展示各种服务(比如团购(deal).直连)正变得越来越流行(图1a), 比如目前 ...

  3. [2]. jekyll安装与应用

    一.ruby安装 这里在win下安装ruby,对应自己电脑的操作系统位数,如我的是64位,对应下载Ruby 2.0.0-p353 (x64)这个版本的ruby.然后安装过程就很简单了: 命令行下输入r ...

  4. [读书笔记]C#学习笔记八:StringBuilder与String详解及参数传递问题剖析

    前言 上次在公司开会时有同事分享windebug的知识, 拿的是string字符串Concat拼接 然后用while(true){}死循环的Demo来讲解.其中有提及string操作大量字符串效率低下 ...

  5. 生成月初月末便于拼接sql

    for ($i=1; $i < 13; $i++) { $date = strtotime(date("2015-$i-01")); $firstday = date(&qu ...

  6. javaweb学习总结(九)—— 通过Servlet生成验证码图片

    一.BufferedImage类介绍 生成验证码图片主要用到了一个BufferedImage类,如下:

  7. U盘启动笔记本无法安装Win7问题和解决

    用“大白菜”工具制作启动U盘,从U盘启动后进入Win PE环境安装Win7,提示“安装win7系统安装程序无法创建新的系统分区,也无法定位现有系统分区”.经以下各种努力后仍无法正常安装: 在BIOS里 ...

  8. 在 Xen 虚拟机下修改系统当前时间

    在 Xen 虚拟机下修改系统当前时间 Xen 虚拟机默认不允许不同的虚拟机使用不同的系统时间,因此所有虚拟机的系统时间都会同宿主机的系统时间严格同步,用 date 命令修改虚拟机系统时间时虽然提示成功 ...

  9. mysqld.exe 占了400M内存的问题

    最近遇到了服务器总是停机的问题,虽然它自己只有2G的内存,不过实际部署的应用访问量非常小,也不至于2G就不够用,所以开始了给服务器瘦身的计划. 看着任务管理器里面的各个进程,发现吃内存最厉害的是mys ...

  10. reader

    http://git.oschina.net/jayqqaa12/abase-reader https://github.com/JustWayward/BookReader https://gith ...