IoC-Invertion of Control,即控制反转,是一种程序设计思想。html
先初步了解几个概念:设计模式
依赖(Dependency):就是有联系,表示一个类依赖于另外一个类。架构
依赖倒置原则(DIP):设计模式六大原则之一,是一种软件架构设计原则。app
控制反转(IoC):一种软件设计原则,上层对下层的依赖(即底层模块的得到)交给第三方。框架
依赖注入(DI):实现IoC的一种方式、手段。ide
IoC容器:依赖注入的框架,用来映射依赖,管理对象的建立和生存周期。函数
依赖就是有联系,有地方使用它就是有依赖它,下面看一个简单的示例:post
class Program { class BMW { public string Show() { return "宝马"; } } class ChinesePeople { private BMW bmw = new BMW(); public void Run() { Console.WriteLine($"今天开{bmw.Show()}上班"); } } static void Main(string[] args) { ChinesePeople people = new ChinesePeople(); BMW bmw = new BMW(); people.Run(); Console.Read(); } }
上面中国人开着宝马去上班,客户端有使用中国人、宝马汽车两个对象,中国人中有使用对象宝马汽车,咱们能够从中找到三个依赖关系:学习
客户端依赖对象ChinesePeople;atom
客户端依赖对象BMW;
ChinesePeople依赖对象BMW;
过些日子来了新需求,中国人不只要开宝马去上班,还要开奔驰去上班,若是按照上面直接依赖关系的方式去作,咱们就须要修改ChinesePeople类,让它实现一个参数为宝马的重载方法Run()。显然这样不是好的设计,咱们总不能每次新增一种汽车(即修改下层模块)都要去修改ChinesePeople类吧(相对于汽车为上层模块),太麻烦了。。。
先简单分析一下,耦合关系就是依赖关系,若是依赖关系很重,牵一发而动全身,将很难维护扩展,耦合关系越少,系统会越稳定,所以要较少依赖。
定义:
A.高层模块不该依赖于底层模块,二者应该依赖于抽象。
B.抽象不该该依赖于细节,细节应该依赖于抽象。
在这个图中,咱们发现高层模块定义接口,将不直接依赖于下层模块,下层模块负责实现高层模块定义的接口,下面看一下示例:
class Program { interface ICar { string Show(); } class BMW : ICar { public string Show() { return "宝马"; } } class BenZ : ICar { public string Show() { return "奔驰"; } } interface IPeople { void Run(ICar car); } class ChinesePeople : IPeople { public void Run(ICar car) { Console.WriteLine($"今天开{car.Show()}上班"); } } static void Main(string[] args) { ICar bmw = new BMW(); ICar benz = new BenZ(); IPeople people = new ChinesePeople(); people.Run(bmw); people.Run(benz); Console.Read(); } }
运行结果以下:
分析:上面代码中,ChinesePeople类再也不依赖于具体的汽车,而是依赖于汽车的抽象,这样使得无论换什么样的汽车品牌,中国人都是能够开着去上班的,并且不须要修改ChinesePeople类。想一下,这样是否是挺好的,咱们能够得出:上层再也不依赖细节,相比面向实现,面向接口较好,由于抽象相比细节要更稳定。
上面示例中,咱们实现了具体的人和具体的汽车的隔离,具体人只和汽车的接口有关。可是Program中Main方法里的具体对象写死了,控制权变小,当我要修改美国人开着福特去上班时,就不得不要去修改代码,那怎么把控制权转移呢?
下面看一个简单的示例(请先添加System.Configuration引用):
interface ICar { string Show(); }
interface IPeople { void Run(ICar car); }
class BMW : ICar { public string Show() { return "宝马"; } }
class ChinesePeople : IPeople { public void Run(ICar car) { Console.WriteLine($"今天开{car.Show()}上班"); } }
<?xml version="1.0" encoding="utf-8" ?> <configuration> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" /> </startup> <appSettings> <add key="People" value="LinkTo.Test.ConsoleIoC.ChinesePeople,LinkTo.Test.ConsoleIoC"/> <add key="Car" value="LinkTo.Test.ConsoleIoC.BMW,LinkTo.Test.ConsoleIoC"/> </appSettings> </configuration>
class Program { static void Main(string[] args) { #region 反射+配置文件实现Ioc string people = ConfigurationManager.AppSettings["People"]; string car = ConfigurationManager.AppSettings["Car"]; Assembly assemblyPeople = Assembly.Load(people.Split(',')[1]); Assembly assemblyCar = Assembly.Load(car.Split(',')[1]); Type typePeople = assemblyPeople.GetType(people.Split(',')[0]); Type typeCar = assemblyPeople.GetType(car.Split(',')[0]); IPeople ipeople = (IPeople)Activator.CreateInstance(typePeople); ICar icar = (ICar)Activator.CreateInstance(typeCar); ipeople.Run(icar); Console.Read(); #endregion } }
上面代码中,咱们使用反射+配置文件的方式,将对象建立的控制权转移到了配置文件,这就是所谓的控制反转。
分析:控制反转是将对象建立的控制权交给了第三方,能够是IoC容器,它就至关于简单工厂。咱们要什么对象,工厂就给咱们什么对象,这样依赖关系就变了,它们(人和车)都依赖于IoC容器,经过IoC容器创建它们之间的依赖关系。(依赖对象再也不直接经过new来获取)
运行结果以下:
上面说到的控制反转,咱们了解到是将控制权转移,这是咱们的目的。配置文件+反射是一种实现,而依赖注入则提供的是一种思想,或者说是实现IoC的手段。
依赖注入是将对象的建立和绑定转移到被依赖对象的外部来实现,通常使用哪些方法来实现呢?
class ChinesePeopleConstructor { private readonly ICar _car; //依赖注入:构造函数注入 public ChinesePeopleConstructor(ICar car) { _car = car; } public void Run() { Console.WriteLine($"今天开{_car.Show()}上班"); } }
class Program { static void Main(string[] args) { #region 依赖注入:构造函数注入 ICar bmw = new BMW(); ChinesePeopleConstructor people = new ChinesePeopleConstructor(bmw); people.Run(); Console.Read(); #endregion } }
class ChinesePeopleProperty { //依赖注入:属性注入 public ICar Car { get; set; } public void Run() { Console.WriteLine($"今天开{Car.Show()}上班"); } }
class Program { static void Main(string[] args) { #region 依赖注入:属性注入 ICar bmw = new BMW(); ChinesePeopleProperty people = new ChinesePeopleProperty { Car = bmw }; people.Run(); Console.Read(); #endregion } }
interface IDependent { void SetDependent(ICar icar); }
class ChinesePeopleInterface : IDependent { private ICar _car; //依赖注入:接口注入 public void SetDependent(ICar car) { _car = car; } public void Run() { Console.WriteLine($"今天开{_car.Show()}上班"); } }
class Program { static void Main(string[] args) { #region 依赖注入:接口注入 ICar bmw = new BMW(); ChinesePeopleInterface people = new ChinesePeopleInterface(); people.SetDependent(bmw); people.Run(); Console.Read(); #endregion } }
IoC容器是一个DI框架,主要功能有一下几点:
A.动态建立、注入依赖对象;
B.管理对象生命周期;
C.映射依赖关系;
常见的IoC容器:Spring.NET,Castle Windsor, Ninject,Autofac,Unity。。。
在上一篇《C# AOP学习笔记》的【使用EntLib\PIAB Unity实现AOP(带配置)】中,已经使用Unity容器实现了IoC,让咱们再来看看配置文件:
假如只须要IoC不须要AOP,container是这样子的:
<configuration> <configSections> <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Unity.Configuration"/> </configSections> <unity> <sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Unity.Interception.Configuration"/> <containers> <container name="IoCContainer"> <!--注册匹配规则:前面是完整类型名称,后面是所在的dll名称。--> <register type="LinkTo.Test.ConsoleAop.UnityConfigAOP.IUserProcessor,LinkTo.Test.ConsoleAop" mapTo="LinkTo.Test.ConsoleAop.UnityConfigAOP.UserProcessor,LinkTo.Test.ConsoleAop"></register> </container> </containers> </unity> </configuration>
从IoC的注册匹配规则能够看出,前面是完整类型名称,后面是所在的dll。这个就比较厉害了,假如一个系统有扩展的功能或者个性要求,只需配置使用新的dll便可,原有系统不须要改代码。这样,除了很好地符合了"开闭原则"外,对于构建一个可配置可扩展的系统,是一个很是厉害的利器。
参考自:
http://www.javashuo.com/article/p-bverypbb-gz.html
推荐博文: