1、引言设计模式
IOC-Invertion of Control,即控制反转,是一种程序设计思想,世上本没有路,走的人多了便有了路,本文将一步步带你了解IOC设计思想的演进之路。架构
在学习IOC以前咱们先初步了解几个概念app
依赖(Dependency):就是有联系,表示一个类依赖于另外一个类框架
依赖倒置原则(DIP):设计模式六大原则之一,是一种软件架构设计原则ide
控制反转(IOC):一种软件设计原则,上层对下层的依赖(即底层模块的得到)交给第三方函数
依赖注入(DI):实现IOC的一种方式、手段学习
IOC容器:依赖注入的框架,用来映射依赖,管理对象建立和生存周期spa
2、依赖架构设计
依赖就是有联系,有地方使用它就是有依赖它,下面看一个简单的示例设计
class BMW { public string Show() { return "宝马"; } } class ChinesePeople { private BMW bmw = new BMW(); public void Run() { Console.WriteLine($"今天开{bmw.Show()}上班"); } } class Program { static void Main(string[] args) { ChinesePeople people = new ChinesePeople(); BMW bmw = new BMW(); people.Run(); Console.Read(); } }
上面中国人开着宝马去上班,客户端有使用中国人、宝马汽车两个对象,中国人中有使用对象宝马汽车,咱们能够从中找到三个依赖关系:
客户端依赖对象ChinesePeople;
客户端依赖对象BMW;
ChinesePeople依赖对象BMW;
3、依赖倒置原则
过些日子来了新需求,中国人不只要开宝马去上班,还要开奔驰去上班,若是按照上面直接依赖关系的方式去作,咱们就须要修改ChinesePeople类,让它实现一个参数为宝马的重载方法Run(),显然这样不是好的设计,咱们总不能每次新增一种汽车(即修改下层模块)都要去修改ChinesePeople类吧(相对于汽车为上层模块),太麻烦了。。。
先简单分析一下,耦合关系就是依赖关系,若是依赖关系很重,牵一发而动全身,将很难维护扩展,耦合关系越少,系统会越稳定,所以要较少依赖
定义:A.高层模块不该依赖于底层模块,二者应该依赖于抽象
B.抽象不该该依赖于细节,细节应该依赖于抽象
在这个图中,咱们发现高层模块定义接口,将不直接依赖于下层模块,下层模块负责实现高层模块定义的接口,下面看代码demo:
interface ICar { string Show(); } class BMW:ICar { public string Show() { return "宝马"; } } class BenZ : ICar { public string Show() { return "奔驰"; } } interface IPeople { void Run(ICar bmw); } class ChinesePeople :IPeople { public void Run(ICar bmw) { Console.WriteLine($"今天开{bmw.Show()}上班"); } } class Program { static void Main(string[] args) { ICar carBMW = new BMW(); ICar carBenZ = new BenZ(); IPeople people = new ChinesePeople(); people.Run(carBMW); people.Run(carBenZ); Console.Read(); } }
分析:上面代码中,ChinesePeople类再也不依赖于具体的汽车,而是依赖于汽车的抽象,这样使得无论换什么样的汽车品牌,中国人都是能够开着去上班的,并且不须要修改ChinesePeople类。想一下,这样是否是挺好的,咱们能够得出:上层再也不依赖细节,相比面向实现,面向接口较好,由于抽象相比细节要更稳定。
4、控制反转
上面示例中,咱们实现了具体的人和具体的汽车的隔离,具体人只和汽车的接口有关。可是Program中main方法里的具体对象写死了,控制权变小,当我要修改美国人开着福特去上班时,就不得不要去修改代码,那怎么把控制权转移呢?
下面看一个简单的示例:
interface ICar { string Show(); } class BMW:ICar { public string Show() { return "宝马"; } } interface IPeople { void Run(ICar bmw); } class ChinesePeople :IPeople { public void Run(ICar bmw) { Console.WriteLine($"今天开{bmw.Show()}上班"); } } class Program { static void Main(string[] args) { 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(); } } <?xml version="1.0" encoding="utf-8" ?> <configuration> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> </startup> <appSettings> <add key="people" value="MyIOC_IOC.ChinesePeople,MyIOC_IOC"/> <add key="car" value="MyIOC_IOC.BMW,MyIOC_IOC"/> </appSettings> </configuration>
上面代码中,咱们使用反射+配置文件的方式,将对象建立的控制权转移到了配置文件,这就是所谓的控制反转
分析,控制反转是将对象建立的控制权交给了第三方,能够是IOC容器,它就至关于工厂,咱们要什么对象,工厂给咱们什么对象,这样依赖关系就变了,它们(人和车)都依赖于IOC容器,经过IOC容器创建它们之间的依赖关系。(依赖对象再也不被依赖模块的类中直接经过new来获取)
5、依赖注入
上面说到的控制反转,咱们了解到是将控制权转移,这是咱们的目的,配置文件+反射是是一种实现,而依赖注入则提供的是一种思想,或者说是实现IOC的手段。
依赖注入是将对象的建立和绑定转移到被依赖对象的外部来实现。在依赖关系中ChinesePeople类所依赖的对象BMW类的建立和绑定是在ChinesePeople类内部执行的,显然这种方法是不可取的,那咱们怎么BMW类的引用传递给ChinesePeople类呢?
方法一 构造函数注入
interface ICar { string Show(); } class BMW:ICar { public string Show() { return "宝马"; } } class ChinesePeopleContructor { private ICar _car; public ChinesePeopleContructor(ICar bmw) { _car = bmw; } public void Run() { Console.WriteLine($"今天开{_car.Show()}上班"); } } static void Main(string[] args) { ICar car = new BMW(); ChinesePeopleContructor people = new ChinesePeopleContructor(car); people.Run(); Console.Read(); }
分析,BMW类对象的建立和绑定转移到ChinesePeople类的外部来实现,解除了两个对象之间的耦合,当须要开奔驰去上班的时候,只须要定义一个奔驰类,外部从新绑定依赖,不须要修改ChinesePeople类的内部,便可是先中国人开奔驰去上班的需求
方法二 属性注入
interface ICar { string Show(); } class BMW:ICar { public string Show() { return "宝马"; } } class ChinesePeopleProperty { private ICar _ICar; public ICar IC { get { return _ICar; } set { _ICar = value; } } public void Run() { Console.WriteLine($"今天开{_ICar.Show()}上班"); } } static void Main(string[] args) { ICar car = new BMW(); ChinesePeopleProperty people = new ChinesePeopleProperty(); people.IC = car; people.Run(); Console.Read(); }
分析,属性注入是经过给属性赋值,从而传递依赖
方法三 接口注入
interface ICar { string Show(); } class BMW:ICar { public string Show() { return "宝马"; } } interface IDependent { void SetDependent(ICar icar); } class ChinesePeopleInterface : IDependent { private ICar _ICar; public void SetDependent(ICar icar) { _ICar = icar; } public void Run() { Console.WriteLine($"今天开{_ICar.Show()}上班"); } } static void Main(string[] args) { ICar car = new BMW(); ChinesePeopleInterface people = new ChinesePeopleInterface(); people.SetDependent(car); people.Run(); Console.Read(); }
分析,接口依赖是定义一个设置依赖的方法,而后被依赖类继承并实现这个接口
6、IOC容器
IOC容器是一个DI框架,主要功能有一下几点
1.动态建立、注入依赖对象;
2.管理对象生命周期
2.映射依赖关系
常见的IOC容器:Spring.NET,Castle Windsor, Ninject,Autofac,Unity等等。。。
ioc容器提供了不少丰富的API,因为时间和篇幅等关系,我会在下篇博客中和您一块儿学习IOC容器之一Unity,敬请期待,未完待续。。。
不努力一把,坐井观天,将永远不知道本身和别人的差距有多大,身为菜鸟的我相信,天道酬勤,大道至简,最好的成功之道即是坚持、学习、总结。
本文版权归做者和博客园共有,欢迎转载,转载请注明出处。感谢您的阅读。