IOC:英文全称:Inversion of Control,中文名称:控制反转,它还有个名字叫依赖注入(Dependency Injection)。
作用:将各层的对象以松耦合的方式组织在一起,解耦,各层对象的调用完全面向接口。当系统重构的时候,代码的改写量将大大减少。
理解依赖注入:
    当一个类的实例需要另一个类的实例协助时,在传统的程序设计过程中,通常有调用者来创建被调用者的实例。然而采用依赖注入的方式,创建被调用者的工作不再由调用者来完成,因此叫控制反转,创建被调用者的实例的工作由IOC容器来完成,然后注入调用者,因此也称为依赖注入。
举个有意思的例子(来源于互联网)

假如我们要设计一个Girl和一个Boy类,其中Girl有Kiss方法,即Girl想要Kiss一个Boy,首先问题是Girl如何认识Boy?
    在我们中国常见的MM认识GG的方式有以下几种:
    A 青梅竹马    B 亲友介绍   C 父母包办
    哪一种是最好的?

1.青梅竹马:很久很久以前,有个有钱的地主家的一闺女叫Lily,她老爸把她许配给县太爷的儿子Jimmy,属于指腹为婚,Lily非常喜欢kiss,但是只能kiss Jimmy

  1. public class Lily{
  2. public Jimmy jimmy;
  3. public Girl()
  4. {
  5. jimmy=new Jimmy();
  6. }
  7. public void Kiss()
  8. {
  9. jimmy.Kiss();
  10. }
  11. }
  12. public class Jimmy
  13. {
  14. public void Kiss()
  15. {
  16. Console.WriteLine("kissing");
  17. }
  18. }

这样导致Lily对Jimmy的依赖性非常强,紧耦合。

2.亲友介绍:经常Kiss同一个人令Lily有些厌恶了,她想尝试新人,于是与Jimmy分手了,通过亲朋好友(中间人)来介绍

  1. public class Lily{
  2. public Boy boy;
  3. public Girl()
  4. {
  5. boy=BoyFactory.createBoy();
  6. }
  7. public void Kiss()
  8. {
  9. boy.Kiss();
  10. }
  11. }

亲友介绍,固然是好。如果不满意,尽管另外换一个好了。但是,亲友BoyFactory经常是以Singleton的形式出现,不然就是,存在于Globals,无处不在,无处不能。实在是太繁琐了一点,不够灵活。我为什么一定要这个亲友掺和进来呢?为什么一定要付给她介绍费呢?万一最好的朋友爱上了我的男朋友呢?

3.父母包办:一切交给父母,自己不用非吹灰之力,Lily在家只Kiss

  1. public class Lily{
  2. public Boy boy;
  3. public Girl(Boy boy)
  4. {
  5. this.boy=boy;
  6. }
  7. public void Kiss()
  8. {
  9. this.boy.Kiss();
  10. }
  11. }

Well,这是对Girl最好的方法,只要想办法贿赂了Girl的父母,并把Boy交给他。那么我们就可以轻松的和Girl来Kiss了。看来几千年传统的父母之命还真是有用哦。至少Boy和Girl不用自己瞎忙乎了。这就是IOC,将对象的创建和获取提取到外部。由外部容器提供需要的组件。

在设计模式中我们应该还知道依赖倒转原则,应是面向接口编程而不是面向功能实现,好处是:多实现可以任意切换,我们的Boy应该是实现Kissable接口。这样一旦Girl不想kiss可恶的Boy的话,还可以kiss可爱的kitten和慈祥的grandmother

好在.net中微软有一个轻量级的IoC框架Unity,支持构造器注入,属性注入,方法注入如下图所示

具体使用方法如下图所示

  1. using System;
  2. using Microsoft.Practices.Unity;
  3. namespace ConsoleApplication9
  4. {
  5. class Program
  6. {
  7. static void Main(string[] args)
  8. {
  9. //创建容器
  10. IUnityContainer container=new UnityContainer();
  11. //注册映射
  12. container.RegisterType<IKiss, Boy>();
  13. //得到Boy的实例
  14. var boy = container.Resolve<IKiss>();
  15. Lily lily = new Lily(boy);
  16. lily.kiss();
  17. }
  18. }
  19. public interface IKiss
  20. {
  21. void kiss();
  22. }
  23. public class Lily:IKiss
  24. {
  25. public IKiss boy;
  26. public Lily(IKiss boy)
  27. {
  28. this.boy=boy;
  29. }
  30. public void kiss()
  31. {
  32. boy.kiss();
  33. Console.WriteLine("lily kissing");
  34. }
  35. }
  36. public class Boy : IKiss
  37. {
  38. public void kiss()
  39. {
  40. Console.WriteLine("boy kissing");
  41. }
  42. }
  43. }

如果采用配置文件注册的话

  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <configuration>
  3. <configSections>
  4. <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,Microsoft.Practices.Unity.Configuration"/>
  5. </configSections>
  6. <unity>
  7. <containers>
  8. <container name="defaultContainer">
  9. <register type="命名空间.接口类型1,命名空间" mapTo="命名空间.实现类型1,命名空间" />
  10. <register type="命名空间.接口类型2,命名空间" mapTo="命名空间.实现类型2,命名空间" />
  11. </container>
  12. </containers>
  13. </unity>
  14. </configuration>

配置的后台代码:

  1. UnityConfigurationSection configuration = ConfigurationManager.GetSection(UnityConfigurationSection.SectionName)
  2. as UnityConfigurationSection;
  3. configuration.Configure(container, "defaultContainer");

可以通过方法ResolveAll来得到所有注册对象的实例:
var Instances = container.Resolve<IKiss>();

Martin Fowler在那篇著名的文章《Inversion of Control Containers and the Dependency Injection pattern》中将具体依赖注入划分为三种形式,即构造器注入、属性(设置)注入和接口注入,习惯将其划分为一种(类型)匹配和三种注入:

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

我们创建一个控制台程序,定义如下几个接口(IA、IB、IC和ID)和它们各自的实现类(A、B、C、D)。在类型A中定义了3个属性B、C和D,其类型分别为接口IB、IC和ID。其中属性B在构在函数中被初始化,以为着它会以构造器注入的方式被初始化;属性C上应用了DependencyAttribute特性,意味着这是一个需要以属性注入方式被初始化的依赖属性;属性D则通过方法Initialize初始化,该方法上应用了特性InjectionMethodAttribute,意味着这是一个注入方法在A对象被IoC容器创建的时候会被自动调用。

  1. public interface IA { }
  2. public interface IB { }
  3. public interface IC { }
  4. public interface ID { }
  5. public class A : IA
  6. {
  7. public IB B { get; set; }
  8. [Dependency]
  9. public IC C { get; set; }
  10. public ID D { get; set; }
  11. public A(IB b)
  12. {
  13. this.B = b;
  14. }
  15. [InjectionMethod]
  16. public void Initalize(ID d)
  17. {
  18. this.D = d;
  19. }
  20. }
  21. public class B : IB { }
  22. public class C : IC { }
  23. public class D : ID { }

然后我们为该应用添加一个配置文件,并定义如下一段关于Unity的配置。这段配置定义了一个名称为defaultContainer的Unity容器,并在其中完成了上面定义的接口和对应实现类之间映射的类型匹配。

  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <configuration>
  3. <configSections>
  4. <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,Microsoft.Practices.Unity.Configuration"/>
  5. </configSections>
  6. <unity>
  7. <containers>
  8. <container name="defaultContainer">
  9. <register type="UnityDemo.IA,UnityDemo" mapTo="UnityDemo.A, UnityDemo"/>
  10. <register type="UnityDemo.IB,UnityDemo" mapTo="UnityDemo.B, UnityDemo"/>
  11. <register type="UnityDemo.IC,UnityDemo" mapTo="UnityDemo.C, UnityDemo"/>
  12. <register type="UnityDemo.ID,UnityDemo" mapTo="UnityDemo.D, UnityDemo"/>
  13. </container>
  14. </containers>
  15. </unity>
  16. </configuration>

最后在Main方法中创建一个代表IoC容器的UnityContainer对象,并加载配置信息对其进行初始化。然后调用它的泛型的Resolve方法创建一个实现了泛型接口IA的对象。最后将返回对象转变成类型A,并检验其B、C和D属性是否是空

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. UnityContainer container = new UnityContainer();
  6. UnityConfigurationSection configuration = ConfigurationManager.GetSection(UnityConfigurationSection.SectionName) as UnityConfigurationSection;
  7. configuration.Configure(container, "defaultContainer");
  8. A a = container.Resolve<IA>() as A;
  9. if (null!=a)
  10. {
  11. Console.WriteLine("a.B==null?{0}",a.B==null?"Yes":"No");
  12. Console.WriteLine("a.C==null?{0}", a.C == null ? "Yes" : "No");
  13. Console.WriteLine("a.D==null?{0}", a.D == null ? "Yes" : "No");
  14. }
  15. }
  16. }

从如下给出的执行结果我们可以得到这样的结论:通过Resolve<IA>方法返回的是一个类型为A的对象,该对象的三个属性被进行了有效的初始化。这个简单的程序分别体现了接口注入(通过相应的接口根据配置解析出相应的实现类型)、构造器注入(属性B)、属性注入(属性C)和方法注入(属性D)

  a.B == null ? No
 a.C == null ? No
 a.D == null ? No

出处:https://www.cnblogs.com/zhangchenliang/archive/2013/01/08/2850970.html

https://www.cnblogs.com/artech/archive/2011/09/15/UnityDemo.html

学习Unity -- 理解依赖注入(IOC)三种方式依赖注入的更多相关文章

  1. ASP.NET MVC中使用Unity进行依赖注入的三种方式

    在ASP.NET MVC中使用Unity进行依赖注入的三种方式 2013-12-15 21:07 by 小白哥哥, 146 阅读, 0 评论, 收藏, 编辑 在ASP.NET MVC4中,为了在解开C ...

  2. Spring静态注入的三种方式

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/chen1403876161/article/details/53644024Spring静态注入的三 ...

  3. SSH深度历险记(八) 剖析SSH核心原则+Spring依赖注入的三种方式

           于java发育.一类程序猿必须依靠类的其他方法,它是通常new依赖类的方法,然后调用类的实例,这样的发展问题new良好的班统一管理的例子.spring提出了依赖注入的思想,即依赖类不由程 ...

  4. SSH深度历险(八) 剖析SSH核心原理+Spring依赖注入的三种方式

           在java开发中,程序员在某个类中需要依赖其它类的方法,则通常是new一个依赖类再调用类实例的方法,这种开发存在的问题是new的类实例不好统一管理,spring提出了依赖注入的思想,即依 ...

  5. Spring注解依赖注入的三种方式的优缺点以及优先选择

    当我们在使用依赖注入的时候,通常有三种方式: 1.通过构造器来注入: 2.通过setter方法来注入: 3.通过filed变量来注入: 那么他们有什么区别吗?应该选择哪种方式更好? 三种方式的区别小结 ...

  6. 详细理解servlet实现的三种方式和生命周期

    阅读目录 开发servlet的三种方式 理解实现servlet接口的方式,理解servlet生命周期 Servlet接口有五个方法 继承GenericServlet 继承HttpServlet 现在很 ...

  7. spring学习(03)之bean实例化的三种方式

    bean实体例化的三种方式 在spring中有三中实例化bean的方式: 一.使用构造器实例化:(通常使用的一个方法,重点) 二.使用静态工厂方法实例化: 三.使用实例化工厂方法实例化 第一种.使用构 ...

  8. 0036 Java学习笔记-多线程-创建线程的三种方式

    创建线程 创建线程的三种方式: 继承java.lang.Thread 实现java.lang.Runnable接口 实现java.util.concurrent.Callable接口 所有的线程对象都 ...

  9. Java反射学习-2 - 获取Class对象的三种方式

    package cn.tx.reflect; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import ...

  10. python核心高级学习总结3-------python实现进程的三种方式及其区别

    python实现进程的三种方式及其区别 在python中有三种方式用于实现进程 多进程中, 每个进程中所有数据( 包括全局变量) 都各有拥有⼀份, 互不影响 1.fork()方法 ret = os.f ...

随机推荐

  1. sublime设置html在浏览器打开

    1.快捷键 Ctrl+Shift+P输入:pcip选择第一个 2.输入:View In Browser安装此插件 3.菜单栏Preferences->Key Bindings  输入:[{ &q ...

  2. [Hibernate] hibernate.cfg.xml 配置文件的一些设置

    <!-- 消除:Disabling contextual LOB creation as createClob() method threw error : java.lang.reflect. ...

  3. SQLLITE HELPER

    using System;using System.Data.SQLite; namespace SQLiteSamples{    class Program    {        //数据库连接 ...

  4. python:assert

    assert 断言 使用assert断言是个好习惯 在没完善一个程序之前,我们不知道程序在哪里会出错,与其让它在运行最崩溃,不如在出现错误条件时就崩溃,这时候就需要assert断言的帮助. asser ...

  5. 实现首字母或拼音检索-sql语句方式

    create function [dbo].[fn_GetPY](@str nvarchar(max),@type int) returns nvarchar(max) as begin ) begi ...

  6. Shell 与正则表达式part1

    1.什么是shell? shell代表两个层面的意思,一个是命令解释器,比如:BASH,另外一个是shell脚本(利用shell的功能所写的一个程序,这个程序是使用纯文本文件,将一些shell的语法与 ...

  7. SQL-46 在audit表上创建外键约束,其emp_no对应employees_test表的主键id。

    题目描述 在audit表上创建外键约束,其emp_no对应employees_test表的主键id.CREATE TABLE employees_test(ID INT PRIMARY KEY NOT ...

  8. JavaWeb基础-servlet

    Servlet简介 Sun公司在其API中提供了一个servlet接口,用户若想用发一个动态web资源(即开发一个Java程序向浏览器输出数据),需要完成以下2个步骤: 1.编写一个Java类,实现s ...

  9. Problem D: 平面上的点——Point类 (IV)

    Description 在数学上,平面直角坐标系上的点用X轴和Y轴上的两个坐标值唯一确定.现在我们封装一个“Point类”来实现平面上的点的操作. 根据“append.cc”,完成Point类的构造方 ...

  10. effective java——12考虑实现coparable接口

    float和double类型的主要设计目标是为了科学计算和工程计算.他们执行二进制浮点运算,这是为了在广域数值范围上提供较为精确的快速近似计算而精心设计的.然而,它们没有提供完全精确的结果,所以不应该 ...