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的主要内容,如果未能解决你的问题,请参考以下文章