控制反转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. C语言宏高级用法 [总结]

    1.前言  今天看代码时候,遇到一些宏,之前没有见过,感觉挺新鲜.如是上网google一下,顺便总结一下,方便以后学习和运用.C语言程序中广泛的使用宏定义,采用关键字define进行定义,宏只是一种简 ...

  2. 编码剖析Spring管理bean的原理

    project目录 MyClassPathXMLApplicationContext读取xml,以及实例化bean. 因为是一开始实例化配置文件所有bean,所以需要构造器完成这些工作. packag ...

  3. CentOS挂载NTFS移动硬盘

    CentOS操作系统默认无法挂在NTFS格式的移动硬盘,解决方案之一为使用ntfs-3g挂在: 1. 在其官网上下载安装包: http://www.tuxera.com/community/open- ...

  4. C语言实现二叉树-02版

    ---恢复内容开始--- 昨天,提交完我们的二叉树项目后,今天早上项目经理早早给我打电话: 他说,小伙子干的不错.但是为什么你上面的insert是recusive的呢? 你难道不知道万一数据量大啦!那 ...

  5. SQL Server 之 GROUP BY、GROUPING SETS、ROLLUP、CUBE

    1.创建表 Staff CREATE TABLE [dbo].[Staff]( ,) NOT NULL, ) NULL, ) NULL, ) NULL, [Money] [int] NULL, [Cr ...

  6. 我和Java的故事-------第1弹

    前言:兴趣是学习任何新知识最好的老师 --------------------------------------------------------------------------------- ...

  7. redis 学习指南

    一.介绍 Redis是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久化的日志型.一个高性能的key-value数据库.并提供多种语言的API.说到Key-Value数据库NoSQL数 ...

  8. Android webView打不开网页的解决办法

    在我们开发过程中,有可能会遇到webview有些网页打不开的问题.这可能是设置的不对,下面就是解决办法. 进行如下设置吧,大多数情况都能解决! displayWebview.getSettings() ...

  9. 江豚科技|专业移动APP开发与移动互联网解决方案

    北京江豚科技(www.eoiiioe.com)是国内领先的移动APP开发解决方案服务商,总部在中国的硅谷--中关村,分别在郑州.深圳设有服务机构. 江豚科技承接各类移动app开发外包和软件定制开发,我 ...

  10. 2013年ACM湖南省赛总结

    今年的比赛最大的变化就是改用OJ判题了,相比于PC^2确实省事了不少,至少可以直接复制样例了.题目方面依旧是刘汝佳命题,这点还是相当好的,至少给人以足够的安全感. 开始比赛之后安叔瞬间就把前半部分题目 ...