IOC

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了IOC相关的知识,希望对你有一定的参考价值。

如果你想学懂IOC,你就必须先学懂设计模式的6大原则之中的2种

即:

1、SRP:单一职责

2、DIP:依赖倒置

因为IOC的基础就是这2个模式。

单一职责:

你的代码遵循了单一职责,既每个对象只负责一个职责,对象与对象(程序集、层)之间协同工作(依赖)来完成整个项目。

把整个项目通过分解的形式,形成一个一个小的工能块,像装组装电脑一样,各自负责自己的事情。

依赖倒置:

在 单一职责 的基础之上,我们应该如何处理相互依赖呢?解决办法就是用接口和抽像,而不是具体的对象与对象之间,上下层之间,直接依赖(关联)。

因为接口可以视为一个标准,只要符合的对象都可以使用,而不是具体的写死某个对象。试想一下你的电脑如果内存、硬盘、CPU都焊死在主板上,后果是什么呢?就是不可维护。那么主板只需要定义好,内存\\CPU\\DISK的插槽就可以了。这些硬件在符合接口约束的情况下就可以自由升级。程序设计也是这个道理。

 

我们以一个消息服务为例:

MessageService 依赖的是接口 IMessage
            MessageService MessageService1 = new MessageService();
            MessageService1.Send("你好呀!");

    class MessageService
    {
        private IMessage Message;
        public MessageService()
        {
            Message = new WeiXin();
        }

        public void Send(string value)
        {
            Message.Send(value);
        }
    }


    interface IMessage
    {
        void Send(string value);
    }


    class WeiXin : IMessage
    {
        public void Send(string value)
        {
            Console.WriteLine("WeiXin:{0}", value);
        }
    }

 

在构造函数中 Message = new WeiXin(); 直接依赖,如果项目更改成用 电话通知呢?我们必须要修改消息服务类。

我们把它升级为:

            MessageService MessageService1 = new MessageService(new WeiXin());
            MessageService1.Send("你好呀!");
    class MessageService
    {
        private IMessage Message;
        public MessageService(IMessage message)
        {
            Message = message;
        }

        public void Send(string value)
        {
            Message.Send(value);
        }
    }

 

这种由调用端传入依赖实体的方式,就叫做依赖注入(是由它的上层来指定所依赖的具体对象)

 

如果需求更改,变成 电话通知呢?改调用端代码

MessageService MessageService2 = new MessageService(new TelPhone());
            MessageService2.Send("你好呀!");

这样调用端是不稳定的,有没有一种方法解决这个难受的问题呢?有,就是工厂系列模式

            string heroTypeReflector = System.Configuration.ConfigurationManager.AppSettings["heroTypeReflector"];

            string assemblyName = heroTypeReflector.Split(\',\')[0];
            string typeName = heroTypeReflector.Split(\',\')[1];
            IMessage  message =(IMessage)Activator.CreateInstance(assemblyName, typeName).Unwrap();

            MessageService MessageService3 = new MessageService(message);
            MessageService3.Send("你好呀!");

这样再也不怕升级了。当升级的时候,只需要增加相对应的配置文件,和符合调用接口的 IMessage  实体。

调用端(上端)也变的稳定了。

这一系列的思路就叫做IOC

IOC:控制反转(就是把依赖的选择权交由第三方(通常是配置文件)来做)

它为相互依赖的组件提供抽象,将依赖(低层模块)对象的获得交给第三方(系统)来控制,即依赖对象不在被依赖模块的类中直接通过new来获取。

IOC有2种常见的实现方式:

1、DI:依赖注入DI是Dependency Injection缩写,它提出了“哪些东东的控制权被反转了,被转移了?”,它也给出了答案:“依赖对象的创建获得被反转”。

把模块看成是一个独立的功能,它的依赖是由自己控制,也可以由外部控制。如果它由外部来控制,这个过程就叫做依赖注入。
这种方式是一种可插拔的模块化思路。也叫做依赖倒置原则。也叫解除耦合原则。

2、服务定位

 

,控制反转只是一种开发思想,意思是类的创建 new 的过程叫控制、反转就是将这个权力交出去,没有具体的解决方案。

那么控制反转的具体实现方案有哪些呢?

目前,比较流行的Ioc容器有以下几种:

1. Ninject: http://www.ninject.org/

2. Castle Windsor: http://www.castleproject.org/container/index.html

3. Autofac: http://code.google.com/p/autofac/

4. StructureMap: http://docs.structuremap.net/

5. Unity: http://unity.codeplex.com/

注:根据园友 徐少侠 的提醒,MEF不应该是IoC容器。我又查阅了一些资料,觉得MEF作为IoC容器是有点勉强,它的主要作用还是用于应用程序扩展,避免生成脆弱的硬依赖项。

6. MEF: http://msdn.microsoft.com/zh-cn/library/dd460648.aspx

另外,园友 aixuexi 提出Spring.NET也是比较流行的IoC容器。

7. Spring.NET: http://www.springframework.net/

园友 wdwwtzy 也推荐了一个不错的IoC容器:

8. LightInject: http://www.lightinject.net/ (推荐使用Chrome浏览器访问)

 

这里我们要讲的是微软的Unity

Unity 是一种容器,为什么叫它容器呢?大概意思是它集中管理了一系列的注入过程。

 

Unity 容器的6个主要功能

1. 它提供了创建(或者装配)对象实例的机制(接口),而这些对象实例可能还包含了其它被依赖的对象实例。(属性、构造、函数)

2. Unity允许将预先配置的对象注入到类中(一般为配置文件),实现了inversion of control (IoC)的功能。在Unity中,支持constructor injection(构造器注入), property setter injection(属性设值注入)以及method call injection(方法注入)。ps:这里的方法注入与Martin Fowler定义的接口注入类似而稍有区别。

3. 支持容器的体系结构。一个容器可以有子容器,允许从子容器到父容器的对象定位查询。(?这个我不懂)


4. 可以通过配置文件进行准备和配置容器。

5. 不会影响到类的定义(属性设值注入和方法注入除外),这也是轻量级容器的一个体现。(?这个我不懂)

6. 支持自定义的容器扩展。(?这个我不懂)

 

Unity 有5种使用场景

1、接口注入

            container.RegisterType<IMessage, WeiXin>();
            container.RegisterType<IMessageService, MessageService>();

            IMessageService IMessageService1 = container.Resolve<IMessageService>();
            IMessageService1.Send("你好呀!");

将 IMessageService 注册到 MessageService

将 IMessage 注册到 WeiXin

类型映射是我们无法回避的一个问题,同时也是一系列工厂模式的根本出发点

如果需要使用同样的接口(或基类)注册多个映射,可以指定名称来区分每个映射。

如果是泛型在接口后面加一个 `1

2、属性注入[Dependency]

        [Dependency]
        public ILogger log
        {
            get { return logger; }

            set { logger = value; }
        }

 

3、构造注入[InjectionConstructor]

        [InjectionConstructor]
        public MessageService(IMessage message)
        {
            Message = message;
        }

 

对于构造器注入,Unity支持两种方式的依赖注入:

1、自动依赖注入

2、通过打标签标识。[InjectionConstructor]

具体来说,对于只有单一构造函数的目标类,可以使用自动依赖注入的方式,而对于有多个构造函数的目标类,则应该使用在构造函数上打标签的方式来进行注入。

这是因为对于具有多个构造器的目标类,如果没有发现标有InjectionConstructor标签的构造器,Unity会自动使用参数最多的构造器。如果参数最多的构造有多个,就会报错。

 

4、函数注入[InjectionMethod]

        [InjectionMethod]
        public void Inject(ILogger ilogger1)
        {
            ILogger1 = ilogger1;
        }

 

5、单例模式

container.RegisterType<IMessageService, MessageService>(new ContainerControlledLifetimeManager());

把一个已经存在的对象注册成单例模式

container.RegisterType<CommonOrder>(new ContainerControlledLifetimeManager());

把一个已经存在的对象注册成单例模式

            container.RegisterType<CommonOrder>("s1", new ContainerControlledLifetimeManager());
            container.RegisterType<CommonOrder>("s2", new ContainerControlledLifetimeManager());

调用

CommonOrder CommonOrder1 = container.Resolve<CommonOrder>("s1");
CommonOrder CommonOrder2 = container.Resolve<CommonOrder>("s2");

 

为了实现单例模式,我们通常的做法是,在类中定义一个私有构造函数,和一个静态公开只读自身字段。但是我觉得这种做法将对象的生命周期管理与类本身耦合在了一起,与SRP:单一职责原则相违背。所以我觉得遇到需要使用单例的地方,应该将生命周期管理的职责转移到对象容器上,而我们的类依然是一个干净的类。

将Unity应用于单例模式,主要有两种使用场景:

(1)将类型注册为单例;

(2)将已有对象注册为单例。


默认情况下,直接使用RegisterType(不指定LifetimeManager),然后Resolve所得到的对象的生命周期是短暂的,容器并不保存对象的引用,每次调用Resolve方法我们都会得到一个新对象。然而,当我们指定了ContainerControlledLifetimeManager参数后,容器在第一次调用Resolve方法时会创建一个新对象,此后在每次调用Resolve时,都会返回之前创建的对象。


除此之外,Unity还扩展了单例的使用方式。例如,我需要把某个类设计为单例,但是发现在应用中我们需要这个类的两个单例,分别有不同的用途,这时,使用Unity的类型与名称组合的标识键,就可以满足这种使用场景。

 

 

用配置文件 

 

    /// <summary>
    /// 这个地方的DI依赖注入实际为后面的处理器(Processor)服务
    /// </summary>
    public class DIFactory
    {
        private static string ContainerName = "DefaultContainer";
        private static Dictionary<string, IUnityContainer> ContainersDictionary = new Dictionary<string, IUnityContainer>();

        /// <summary>
        /// 静态的构造函数就是初使化的时候,装载这些内容
        /// </summary>
        static DIFactory()
        {
            //配置UnityContainer
            IUnityContainer container = new UnityContainer();
            //配置文件管理类
            ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
            ///读取配置文件
            fileMap.ExeConfigFilename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, StaticConstant.UnityConfigPath);
            ///装载到 Configuration 中
            Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
            ////装载 UnityConfigurationSection 配置节点
            UnityConfigurationSection configSection = (UnityConfigurationSection)configuration.GetSection(UnityConfigurationSection.SectionName);
            ///装载到 IUnityContainer 容器内部
            configSection.Configure(container, ContainerName);
            ///ContainersDictionary 管理字典
            ContainersDictionary.Add(ContainerName, container);
        }

        /// <summary>
        /// 从 ContainersDictionary 管理 中 根据容器名称获取container  
        /// </summary>
        /// <param name="containerName"></param>
        /// <returns></returns>
        public static IUnityContainer GetUnityContainer(string containerName = null)
        {
            if (string.IsNullOrEmpty(containerName))
            {
                containerName = ContainerName;
            }
            return ContainersDictionary[containerName];
        }
    }

 

 

<configuration>
    <configSections>
        <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/>
    </configSections>
    <unity>
        <containers>
            <container name="DefaultContainer">
                <register type="System.Data.Entity.DbContext,EntityFramework" mapTo="Bussiness.Service.HBContext, Bussiness.Service"/>
                <register type="Bussiness.Interface.ISysUserService, Bussiness.Interface" mapTo="Bussiness.Service.SysUserService,Bussiness.Service"/>
                <register type="Bussiness.Interface.ISysVisitTemplateService, Bussiness.Interface" mapTo="Bussiness.Service.SysVisitTemplateService,Bussiness.Service"/>
                <register type="Bussiness.Interface.ISysVisitTemplateDetailService, Bussiness.Interface" mapTo="Bussiness.Service.SysVisitTemplateDetailService,Bussiness.Service"/>
                <register type="Bussiness.Interface.IBaseService`1, Bussiness.Interface" mapTo="Bussiness.Service.BaseService`1,Bussiness.Service"/>
                <register type="Bussiness.Interface.ILogService, Bussiness.Interface" mapTo="Bussiness.Service.LogService,Bussiness.Service"/>
                <register type="Bussiness.Interface.ICIChatService, Bussiness.Interface" mapTo="Bussiness.Service.CIChatService,Bussiness.Service"/>
                <register type="Bussiness.Interface.ICICommentService, Bussiness.Interface" mapTo="Bussiness.Service.CICommentService,Bussiness.Service"/>
                <register type="Bussiness.Interface.IHDDoctorService, Bussiness.Interface" mapTo="Bussiness.Service.HDDoctorService, Bussiness.Service"/>
                <register type="Bussiness.Interface.IPIPatientService, Bussiness.Interface" mapTo="Bussiness.Service.PIPatientService,Bussiness.Service"/>
                <register type="Bussiness.Interface.IPIPatientExtraService, Bussiness.Interface" mapTo="Bussiness.Service.PIPatientExtraService,Bussiness.Service"/>
                <register type="Bussiness.Interface.IPITreatRecordService, Bussiness.Interface" mapTo="Bussiness.Service.PITreatRecordService,Bussiness.Service"/>
                <register type="Bussiness.Interface.IHDHospitalService, Bussiness.Interface" mapTo="Bussiness.Service.HDHospitalService,Bussiness.Service"/>
                <register type="Bussiness.Interface.IHDHospitalApplyService, Bussiness.Interface" mapTo="Bussiness.Service.HDHospitalApplyService,Bussiness.Service"/>
                <register type="Bussiness.Interface.IHDBedService, Bussiness.Interface" mapTo="Bussiness.Service.HDBedService,Bussiness.Service"/>
                <register type="Bussiness.Interface.IHDRoleMenuService, Bussiness.Interface" mapTo="Bussiness.Service.HDRoleMenuService,Bussiness.Service"/>
                <register type="Bussiness.Interface.IHDDoctorMenuService, Bussiness.Interface" mapTo="Bussiness.Service.HDDoctorMenuService,Bussiness.Service"/>
                <register type="Bussiness.Interface.IHDVisitDetailService, Bussiness.Interface" mapTo="Bussiness.Service.HDVisitDetailService,Bussiness.Service"/>
                <register type="Bussiness.Interface.IHDVisitService, Bussiness.Interface" mapTo="Bussiness.Service.HDVisitService,Bussiness.Service"/>
            </container>
        </containers>
    </unity>
</configuration>

 

 

http://www.cnblogs.com/fuchongjundream/p/3915391.html

 

以上是关于IOC的主要内容,如果未能解决你的问题,请参考以下文章

死磕 Spring----- IOC 之从单例缓存中获取单例 bean

[Spring 源解系列] 重温 IOC 设计理念

Spring之IOC原理及代码详解

一起写框架-控制反转(Ioc)概述

Spring Ioc代码阅读

控制反转(Ioc)