前言
IOC,控制反转(Inversion of Control)的缩写,是一种优秀的设计模式。网上资料一大堆,也看了很多,但是我目前对 IOC 只理解出了一个作用:解耦。
不同的平台,有不同的 IOC 模式的框架,像 Sun ONE 技术体系下的 Spring、Guice、Pico Container、Avalon、HiveMind、JBoss、Jdon、EJB。
像 .Net 技术体系下的 Spring.Net 、Autofac 等等。
这些我都没接触过。写出来纯属想装个逼。
我接触到的第一个 IOC 容器,就是 Castle 的 IOC 容器:Windsor。
IOC 的解耦到底是解了什么耦?怎么操作的? 下面写一个例子来回答我对这两个问题的理解。
一、引言
1,面向接口编程
为什么扯到面向接口编程,因为我觉得 IOC 就是把面向接口编程发挥到极致的工具。
所以先看一个面向接口编程的例子:C# 接口的正确使用方式 。
在例子中,我们编写 Duplicator 类的时候,他的参数是接口,不必知道接口具体是怎么实现的,甚至不必知道实现接口的类的具体类名,直接把接口里面定义的方法拿来用就可以了:
如果要修改 IReader 或者 IWriter 实现里的代码, Duplicator 类可以不做任何改变,这很 “解耦”。
然后我们再看一下 Duplicator 的实例化对象 : duplicator,如下图所示,他的参数,是具体对象(Reader 类是 IReader 的实现,HpWriter 类是 IWriter 的实现):
注意红色箭头标注的地方,也就是说在这里,Main 方法和具体对象耦合了。耦合是一个很讨人嫌的事,如果我们能够跟编写 Duplicator 类一样,用接口代替实例,那在这里不就实现解耦了嘛! 于是有了以下代码:
可以看到 visual studio 标出了很多错误,最关键的错误就是 “ “IReader” 是一个类型,这在给定的上下文中无效”。
在形参中,可以使用接口,但是在实参中,就不可以使用接口了。道理很简单,形参中,不是具体的类,不依赖具体的实现。但是在具体的类中,就要给出实现,不然编译器怎么知道这个接口的实现是哪个。
一般来说,这个耦合是解不开了。但是使用 IOC ,就可以解开这个耦合:
二、Windsor IOC 入门
在项目中引入 Windsor IOC,实现解耦:
1,右键 项目 -> 管理 NuGet 程序包,搜索 “castle.windsor”,不出意外的话应该就在搜索结果的第一个,点击安装。
2,安装之后,可能还要在 NuGet 里更新一下 Castle.Core。
3,引入 IOC 之后,就可以这么写了:
using Castle.MicroKernel.Registration; using Castle.Windsor; using Interface_test.Implement; using Interface_test.Interface; using System; namespace Interface_test { class Program { static void Main(string[] args) { Console.Write("Input: "); //声明容器 var contanier = new WindsorContainer(); //注册接口和实现 contanier.Register(Component.For<IReader>().ImplementedBy<Reader>()); contanier.Register(Component.For<IWriter>().ImplementedBy<Writer>()); Duplicator duplicator = new Duplicator(); //1,直接耦合 duplicator.Copy(new Reader(),new Writer()); //2,从容器中取用对象 duplicator.Copy(contanier.Resolve<IReader>(), contanier.Resolve<IWriter>()); Console.ReadLine(); } } }
代码中,1 和 2 的效果一样,但是 2 中,是不是就没有写具体的实现呢。
IOC 的办法就是,先注册,告诉容器哪个接口对应哪个实现,然后在以后对实现的使用中,就可以直接写接口了,IOC 容器会帮我们取出合适的实现 “ 注入” 到实例里。实际上我们提前告诉了 IOC 哪个接口和哪个实现是绑定在一起的,所以叫 “依赖”。“依赖注入” 就是这么来的。
有人可能会说,引入 IOC 后,代码比之前复杂,还要提前注册接口和实现,这还不是耦合嘛。
没错,这确实是耦合,如果某个代码块和其他程序没有一点耦合,那这个代码块就可以直接删掉了。所以 IOC 并不是完全不耦合,而是弱化耦合,把耦合推迟,从开发阶段,推迟到配置阶段。
在实际的开发中,注册接口和实现的这部分代码,会写成配置文件,程序启动的时候加载配置文件,就知道接口和实现的对应关系了。如果要替换实现,只要修改配置文件就可以了。硬编码变成了软编码。
怎么把注册写到配置文件里,请参阅 Castle IOC:Windsor (二)