IOC控制反转——基础概念与实例

简介: IOC控制反转——基础概念与实例

 IOC(Inversion of Control)控制反转——为了符合依赖倒置原则,也就是高层模块不依赖于低层模块,他们都应该依赖于抽象;抽象不应该依赖于具体,具体依赖于抽象。例如小时候一家一户水井,人们喝水依赖水井和地下水,如果搬迁就要重新打井,水污染就没水喝,这是反依赖倒置;引入自来水厂以后,人们随意搬迁都能喝到水,接一根水管就行,当地下水污染,自来水厂也可以选择其他水源。也就是使用接口,但是总归接口是要被实现的,耦合也就不可避免的产生了。

 通过IOC模式可以解决这个问题,把耦合放到XML文件,通过一个容器在需要的时候再把依赖关系形成,即把需要的接口实现注入到需要它的类。反射应该也是IOC的一个实现的方法。IOC的特点是通过文本的配置文件进行应用程序组件间相互关系的配置,不用修改和编译具体的代码。

 依赖注入(Dependency Injection)是IOC的一种运行模式,把需要的接口实现注入到需要它的类中。

依赖注入的方法有很多:


构造器注入(Constructor Injection):Ioc容器会智能地选择选择和调用适合的构造函数以创建依赖的对象。如果被选择的构造函数具有相应的参数,Ioc容器在调用构造函数之前解析注册的依赖关系并自行获得相应参数对象;


属性注入(Property Injection):如果需要使用到被依赖对象的某个属性,在被依赖对象被创建之后,Ioc容器会自动初始化该属性;


方法注入(Method Injection):如果被依赖对象需要调用某个方法进行相应的初始化,在该对象创建之后,Ioc容器会自动调用该方法。


 Unity提供了对象的容器,那么这个容器是如何进行索引的呢?也就是说,容器内的单元是如何标识的呢?在Unity中,标识主要有两种方式, 一种是直接使用接口(或者基类)作为标识键,另一种是使用接口(或者基类)与名称的组合作为标识键,键对应的值就是具体类。


 第一种:使用接口(或者基类)作为标识键:

  //IWaterTool就是作为标识键,也可以使用基类或是抽象类作为标示,获取注册对象:container.Resolve<IWaterTool>()
  container.RegisterType< IWaterTool, PressWater>();

  第二种:如果一个Ioc容器容器里面注册了多个接口或是基类标示,我们再这样获取就不知道注册的是哪一个?怎么解决,就是用接口或是基类与名称作为标识键,示例代码如下:

  container.RegisterType<IWaterTool, PressWater>("WaterTool1");//注册依赖对象WaterTool1
  container.RegisterType<IWaterTool, PressWater>("WaterTool2");//注册依赖对象WaterTool2
  IWaterTool wt = container.Resolve<IWaterTool>("WaterTool1");//返回依赖对象WaterTool1
  var list = container.ResolveAll<IWaterTool>();//返回所有注册类型为IWaterTool的对象

  在配置文件的映射关系加入name(待考证)

  <register type="IOCDependency.IWaterTool,IOCDependency" name="WaterTool1" mapTo="IOCDependency.PressWater,IOCDependency" />

  构造器注入例子如下: 使用构造器注入需要在在构造函数中传递一个抽象参数,Ioc会自动解析具象所依赖的抽象并注册给具象,还是用上篇喝水作为示例。

  点击“管理NuGet程序包”,安装“Unity”

  分别定义人、水源和获取水源方式的接口。

        /// <summary>
        /// 人接口:喝水,无返回值
        /// </summary>
        public interface IPeople
        {
            void DrinkWater();
        }
        /// <summary>
        /// 获取水方式接口
        /// </summary>
        public interface IWaterTool
        {
            IWater returnWater();
        }
        /// <summary>
        /// 水接口
        /// </summary>
        public interface IWater
        { }

  创建村民类,实现人的接口。

    /// <summary>
    /// 村民
    /// </summary>
    public class VillagePeople : IPeople
    {
        IWaterTool _pw;
        public VillagePeople(IWaterTool pw)
        {
            _pw = pw;//传递抽象参数
        }
        //实现接口
        public void DrinkWater() 
        {
            IWater uw = _pw.returnWater();
            if (uw != null)
            {
                Console.WriteLine("水好甜啊!!!");
                Console.ReadLine();
            }
        }
    }

  创建地下水类,实现水源接口。

    /// <summary>
    /// 地下水
    /// </summary>
    public class UndergroundWater : IWater
    {
    }

  创建压水井类,实现获取水方式接口。

    /// <summary>
    /// 压水井
    /// </summary>
    public class PressWater : IWaterTool
    {
        public IWater returnWater()
        {
            return new UndergroundWater();
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            UnityContainer container = new UnityContainer();//创建一个Unity容器
            //在容器中注册一种类型,即决定用什么用什么做水源,需要注册一下。键是IWaterTool,值为PressWater
            container.RegisterType< IWaterTool, PressWater>();
            //Resolve:将容器的类型输送到VillagePeople
            IPeople people = container.Resolve<VillagePeople>();
            people.DrinkWater();
        }
    }

  也可以使用非泛型注入,代码如下:

        static void Main(string[] args)
        {
            UnityContainer container = new UnityContainer();//创建容器
            container.RegisterType(typeof(IWaterTool), typeof(PressWater));//注册依赖对象
            IPeople people = (IPeople)container.Resolve(typeof(VillagePeople));//返回调用者
            people.DrinkWater();//喝水
       }

 属性注入例子如下: 属性注入只需要在属性字段前面加[Dependency]标记就行了,这次不使用RegisterType方法注册,而是使用配置文件注册。下面用UnityConfigurationSection的Configure方法加载配置文件注册:

 点击“管理NuGet程序包”,安装“Unity.Container”

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <!-- 声明unity配置节,type一定要写对-->
  <configSections>
    <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,Unity.Configuration" />
  </configSections>
  <unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
    <!-- 容器 -->
    <containers>
      <container name="defaultContainer">
        <!--映射关系,type=命名空间,程序集-->
        <register type="IOCDependency.IWaterTool,IOCDependency" mapTo="IOCDependency.PressWater,IOCDependency" />
        <register type="IOCDependency.IPeople,IOCDependency" mapTo="IOCDependency.VillagePeople,IOCDependency" />
      </container>
    </containers>
  </unity>
</configuration>

  改改村民和主程序的代码

    /// <summary>
    /// 村民
    /// </summary>
    public class VillagePeople : IPeople
    {
        [Dependency]//属性注入
        public IWaterTool _pw { get; set; }
        public void DrinkWater()
        {
            Console.WriteLine(_pw.returnWater());
            Console.ReadKey();
        }
    }
        static void Main(string[] args)
        {
            UnityContainer container = new UnityContainer();//创建容器
            UnityConfigurationSection configuration =  (UnityConfigurationSection)ConfigurationManager.GetSection(UnityConfigurationSection.SectionName);
            configuration.Configure(container, "defaultContainer");;
            //container.Config("Unity.config");
            IPeople people = container.Resolve<IPeople>();//返回调用者
            people.DrinkWater();//喝水
        }

 方法注入例子如下: 方法注入和属性方式使用一样,方法注入只需要在方法前加[InjectionMethod]标记就行了,从方法注入的定义上看,只是模糊的说对某个方法注入,并没有说明这个方法所依赖的对象注入,所依赖的对象无非就三种:参数、返回值和方法内部对象引用,下面做一个示例:

/// <summary>
    /// 村民
    /// </summary>
    public class VillagePeople : IPeople
    {
        public IWaterTool tool;//我是对象引用
        public IWaterTool tool2;//我是参数
        public IWaterTool tool3;//我是返回值
        [InjectionMethod]
        public void DrinkWater()
        {
            if (tool == null)
            { }
        }
        [InjectionMethod]
        public void DrinkWater2(IWaterTool tool2)
        {
            this.tool2 = tool2;
        }
        [InjectionMethod]
        public IWaterTool DrinkWater3()
        {
            return tool3;
        }
    }
        static void Main(string[] args)
        {
            UnityContainer container = new UnityContainer();//创建容器
            UnityConfigurationSection configuration = (UnityConfigurationSection)ConfigurationManager.GetSection(UnityConfigurationSection.SectionName);
            configuration.Configure(container, "defaultContainer");
            VillagePeople people = container.Resolve<IPeople>() as VillagePeople;//返回调用者
            Console.WriteLine("people.tool == null(引用) ? {0}", people.tool == null ? "Yes" : "No");
            Console.WriteLine("people.tool2 == null(参数) ? {0}", people.tool2 == null ? "Yes" : "No");
            Console.WriteLine("people.tool3 == null(返回值) ? {0}", people.tool3 == null ? "Yes" : "No");
            Console.ReadKey();
        }

 其实我们理解的方法注入就是对参数对象的注入(目前不理解这句话。)

相关文章
|
6月前
|
XML Java 数据格式
从六个方面读懂IoC(控制反转)和DI(依赖注入)
在一开始学习 Spring 的时候,我们就接触 IoC 了,作为 Spring 第一个最核心的概念,我们在解读它源码之前一定需要对其有深入的认识,对于初学Spring的人来说,总觉得IOC是模糊不清的,是很难理解的,今天和大家分享网上的一些技术大牛们对Spring框架的IOC的理解以及谈谈我对Spring IOC的理解。
207 2
|
2月前
|
XML Java 数据库
Ioc原理
Ioc原理
42 0
|
5月前
|
Java 调度 容器
心得经验总结:控制反转(IoC)
心得经验总结:控制反转(IoC)
40 0
|
6月前
|
XML Java 程序员
Spring特性之二——IOC控制反转
Spring特性之二——IOC控制反转
57 4
|
6月前
|
Java API 容器
[读书笔记]IOC容器的依赖注入详解
[读书笔记]IOC容器的依赖注入详解
71 0
|
Java 容器 Spring
【Spring源码阅读】IOC容器的依赖注入
SpringIOC容器的依赖注入发生在用户第一次向IOC容器获取Bean时。除在BeanDefinition中设置lazy-init属性让容器完成bean的预实例化。我们在前面《Spring-IOC容器接口设计与功能》中曾讲过,容器BeanFactory通过getBean方法获取Bean。所以这篇文章,我们将从getBean()方法入手分析SpringIOC容器依赖注入的过程。
|
缓存 Oracle 安全
对控制反转理解不深?带你手写一个基于注解的IOC容器 加深对spring底层代码的理解
对控制反转理解不深?带你手写一个基于注解的IOC容器 加深对spring底层代码的理解
对控制反转理解不深?带你手写一个基于注解的IOC容器 加深对spring底层代码的理解
|
XML JavaScript Java
阅读Spring源码:IOC控制反转前的处理
阅读Spring源码:IOC控制反转前的处理
127 0
阅读Spring源码:IOC控制反转前的处理
|
XML 设计模式 Java
IoC底层原理
IoC底层原理
149 0
|
C# 容器
控制反转_依赖注入简明教程
控制反转_依赖注入简明教程
122 0