IOC:英文全稱:Inversion of Control,中文名稱:控制反轉,它還有個名字叫依賴注入(Dependency Injection)。
做用:將各層的對象以鬆耦合的方式組織在一塊兒,解耦,各層對象的調用徹底面向接口。當系統重構的時候,代碼的改寫量將大大減小。
理解依賴注入:
當一個類的實例須要另外一個類的實例協助時,在傳統的程序設計過程當中,一般有調用者來建立被調用者的實例。然而採用依賴注入的方式,建立被調用者的工做再也不由調用者來完成,所以叫控制反轉,建立被調用者的實例的工做由IOC容器來完成,而後注入調用者,所以也稱爲依賴注入。
舉個有意思的例子(來源於互聯網)
html
1.兩小無猜:好久好久之前,有個有錢的地主家的一閨女叫Lily,她老爸把她許配給縣太爺的兒子Jimmy,屬於指腹爲婚,Lily很是喜歡kiss,可是隻能kiss Jimmy
編程
- public class Lily{
- public Jimmy jimmy;
- public Girl()
- {
- jimmy=new Jimmy();
- }
- public void Kiss()
- {
- jimmy.Kiss();
- }
- }
- public class Jimmy
- {
- public void Kiss()
- {
- Console.WriteLine("kissing");
- }
- }
這樣致使Lily對Jimmy的依賴性很是強,緊耦合。設計模式
2.親友介紹:常常Kiss同一我的令Lily有些厭惡了,她想嘗試新人,因而與Jimmy分手了,經過親朋好友(中間人)來介紹
app
- public class Lily{
- public Boy boy;
- public Girl()
- {
- boy=BoyFactory.createBoy();
- }
- public void Kiss()
- {
- boy.Kiss();
- }
- }
親友介紹,當然是好。若是不滿意,儘管另外換一個好了。可是,親友BoyFactory常常是以Singleton的形式出現,否則就是,存在於Globals,無處不在,無處不能。實在是太繁瑣了一點,不夠靈活。我爲何必定要這個親友摻和進來呢?爲何必定要付給她介紹費呢?萬一最好的朋友愛上了個人男友呢?框架
3.父母包辦:一切交給父母,本身不用非吹灰之力,Lily在家只Kiss
函數
- public class Lily{
- public Boy boy;
- public Girl(Boy boy)
- {
- this.boy=boy;
- }
- public void Kiss()
- {
- this.boy.Kiss();
- }
- }
Well,這是對Girl最好的方法,只要想辦法賄賂了Girl的父母,並把Boy交給他。那麼咱們就能夠輕鬆的和Girl來Kiss了。看來幾千年傳統的父母之命還真是有用哦。至少Boy和Girl不用本身瞎忙乎了。這就是IOC,將對象的建立和獲取提取到外部。由外部容器提供須要的組件。oop
在設計模式中咱們應該還知道依賴倒轉原則,應是面向接口編程而不是面向功能實現,好處是:多實現能夠任意切換,咱們的Boy應該是實現Kissable接口。這樣一旦Girl不想kiss可惡的Boy的話,還能夠kiss可愛的kitten和慈祥的grandmotherpost
好在.net中微軟有一個輕量級的IoC框架Unity,支持構造器注入,屬性注入,方法注入以下圖所示this
具體使用方法以下圖所示spa
- using System;
- using Microsoft.Practices.Unity;
- namespace ConsoleApplication9
- {
- class Program
- {
- static void Main(string[] args)
- {
- //建立容器
- IUnityContainer container=new UnityContainer();
- //註冊映射
- container.RegisterType<IKiss, Boy>();
- //獲得Boy的實例
- var boy = container.Resolve<IKiss>();
- Lily lily = new Lily(boy);
- lily.kiss();
- }
- }
- public interface IKiss
- {
- void kiss();
- }
- public class Lily:IKiss
- {
- public IKiss boy;
- public Lily(IKiss boy)
- {
- this.boy=boy;
- }
- public void kiss()
- {
- boy.kiss();
- Console.WriteLine("lily kissing");
- }
- }
- public class Boy : IKiss
- {
- public void kiss()
- {
- Console.WriteLine("boy kissing");
- }
- }
- }
若是採用配置文件註冊的話
- <?xml version="1.0" encoding="utf-8" ?>
- <configuration>
- <configSections>
- <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,Microsoft.Practices.Unity.Configuration"/>
- </configSections>
- <unity>
- <containers>
- <container name="defaultContainer">
- <register type="命名空間.接口類型1,命名空間" mapTo="命名空間.實現類型1,命名空間" />
- <register type="命名空間.接口類型2,命名空間" mapTo="命名空間.實現類型2,命名空間" />
- </container>
- </containers>
- </unity>
- </configuration>
配置的後臺代碼:
- UnityConfigurationSection configuration = ConfigurationManager.GetSection(UnityConfigurationSection.SectionName)
- as UnityConfigurationSection;
- configuration.Configure(container, "defaultContainer");
能夠經過方法ResolveAll來獲得全部註冊對象的實例:
var Instances = container.Resolve<IKiss>();
Martin Fowler在那篇著名的文章《Inversion of Control Containers and the Dependency Injection pattern》中將具體依賴注入劃分爲三種形式,即構造器注入、屬性(設置)注入和接口注入,習慣將其劃分爲一種(類型)匹配和三種注入:
- 類型匹配(Type Matching):雖然咱們經過接口(或者抽象類)來進行服務調用,可是服務自己仍是實如今某個具體的服務類型中,這就須要某個類型註冊機制來解決服務接口和服務類型之間的匹配關係;
- 構造器注入(Constructor Injection):IoC容器會智能地選擇選擇和調用適合的構造函數以建立依賴的對象。若是被選擇的構造函數具備相應的參數,IoC容器在調用構造函數以前解析註冊的依賴關係並自行得到相應參數對象;
- 屬性注入(Property Injection):若是須要使用到被依賴對象的某個屬性,在被依賴對象被建立以後,IoC容器會自動初始化該屬性;
- 方法注入(Method Injection):若是被依賴對象須要調用某個方法進行相應的初始化,在該對象建立以後,IoC容器會自動調用該方法。
咱們建立一個控制檯程序,定義以下幾個接口(IA、IB、IC和ID)和它們各自的實現類(A、B、C、D)。在類型A中定義了3個屬性B、C和D,其類型分別爲接口IB、IC和ID。其中屬性B在構在函數中被初始化,覺得着它會以構造器注入的方式被初始化;屬性C上應用了DependencyAttribute特性,意味着這是一個須要以屬性注入方式被初始化的依賴屬性;屬性D則經過方法Initialize初始化,該方法上應用了特性InjectionMethodAttribute,意味着這是一個注入方法在A對象被IoC容器建立的時候會被自動調用。
- public interface IA { }
- public interface IB { }
- public interface IC { }
- public interface ID { }
- public class A : IA
- {
- public IB B { get; set; }
- [Dependency]
- public IC C { get; set; }
- public ID D { get; set; }
- public A(IB b)
- {
- this.B = b;
- }
- [InjectionMethod]
- public void Initalize(ID d)
- {
- this.D = d;
- }
- }
- public class B : IB { }
- public class C : IC { }
- public class D : ID { }
而後咱們爲該應用添加一個配置文件,並定義以下一段關於Unity的配置。這段配置定義了一個名稱爲defaultContainer的Unity容器,並在其中完成了上面定義的接口和對應實現類之間映射的類型匹配。
- <?xml version="1.0" encoding="utf-8" ?>
- <configuration>
- <configSections>
- <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,Microsoft.Practices.Unity.Configuration"/>
- </configSections>
- <unity>
- <containers>
- <container name="defaultContainer">
- <register type="UnityDemo.IA,UnityDemo" mapTo="UnityDemo.A, UnityDemo"/>
- <register type="UnityDemo.IB,UnityDemo" mapTo="UnityDemo.B, UnityDemo"/>
- <register type="UnityDemo.IC,UnityDemo" mapTo="UnityDemo.C, UnityDemo"/>
- <register type="UnityDemo.ID,UnityDemo" mapTo="UnityDemo.D, UnityDemo"/>
- </container>
- </containers>
- </unity>
- </configuration>
最後在Main方法中建立一個表明IoC容器的UnityContainer對象,並加載配置信息對其進行初始化。而後調用它的泛型的Resolve方法建立一個實現了泛型接口IA的對象。最後將返回對象轉變成類型A,並檢驗其B、C和D屬性是不是空
- class Program
- {
- static void Main(string[] args)
- {
- UnityContainer container = new UnityContainer();
- UnityConfigurationSection configuration = ConfigurationManager.GetSection(UnityConfigurationSection.SectionName) as UnityConfigurationSection;
- configuration.Configure(container, "defaultContainer");
- A a = container.Resolve<IA>() as A;
- if (null!=a)
- {
- Console.WriteLine("a.B==null?{0}",a.B==null?"Yes":"No");
- Console.WriteLine("a.C==null?{0}", a.C == null ? "Yes" : "No");
- Console.WriteLine("a.D==null?{0}", a.D == null ? "Yes" : "No");
- }
- }
- }
從以下給出的執行結果咱們能夠獲得這樣的結論:經過Resolve<IA>方法返回的是一個類型爲A的對象,該對象的三個屬性被進行了有效的初始化。這個簡單的程序分別體現了接口注入(經過相應的接口根據配置解析出相應的實現類型)、構造器注入(屬性B)、屬性注入(屬性C)和方法注入(屬性D)
a.B == null ? No
a.C == null ? No
a.D == null ? No