第一次接觸IoC是我在學習MVP框架的時候,搭建一個MVP框架需要用到IoC,那時候就以為IoC就是依賴注入,但在後來的逐步了解中發現那個只是它的別名而已。IoC全稱應該是Inversion of Control,中文稱為控制反轉;而依賴注入的簡稱是DI,全稱是Dependency Injection,個人覺得這兩者也不是那麼一個別名的關系而已,控制反轉給我的感覺更多的是一種思想,而依賴注入卻正好是一種實現方式。那這裡說說概念
以上概念摘自蔣老師的著作《ASP.NET MVC4 框架揭秘》。但是技術這東西又沒必要向理論學術那樣嚴謹,能讓人知曉其中意圖則可,最初UML最初創作處理也是為了達到這個目的。
高內聚低耦合一直是軟件開發中不斷追求的,現在各種框架MVC,MVP等都是為了解耦而誕生的。在我閱讀微軟的開源項目PetShop時發現裡面用了耳熟能詳的三層架構中使用了最初我學C#時不知有什麼作用的一個東西——接口Interface。它的存在很大的目的就是為了解耦,它能使一些比較具體的事物抽象化。那麼本篇所討論的IoC也是使用了接口。
目前有很多IoC框架:Unity,Spring.NeT,Ninject,StructureMap等。Ninject在我實踐MVP的時候用過,Unity在我閱讀蔣老師的著作時了解過,那麼這回我將會嘗試一下之前沒用過的Unity,體驗一下依賴注入。
模擬使用一個三層架構來體驗這個Unity
定義DAL,BLL,UI層的接口IDAL,IBLL,IUI以及實現它們的類DAL,BLL,UI。
1 interface IDAL 2 { 3 DataTable QueryDatas(); 4 } 5 interface IBLL 6 { 7 List<object> GetSomeDatas(); 8 } 9 10 interface IUI 11 { 12 void ShowData(IBLL bll); 13 } 14 15 class DAL : IDAL 16 { 17 18 public DataTable QueryDatas() 19 { 20 DataTable table = new DataTable(); 21 table.Columns.Add("Col"); 22 for (int i = 0; i < 10; i++) 23 { 24 table.Rows.Add(i); 25 } 26 return table; 27 } 28 } 29 class BLL : IBLL 30 { 31 [Dependency] 32 public IDAL dal { get; set; } 33 34 public List<object> GetSomeDatas() 35 { 36 List<object> result = new List<object>(); 37 DataTable table = dal.QueryDatas(); 38 foreach (DataRow row in table.Rows) 39 { 40 result.Add(row[0]); 41 } 42 return result; 43 } 44 } 45 class UI : IUI 46 { 47 [InjectionMethod] 48 public void ShowData(IBLL bll) 49 { 50 List<object> datas = bll.GetSomeDatas(); 51 foreach (object item in datas) 52 { 53 Console.WriteLine(item); 54 } 55 } 56 }
在上面的代碼中BLL類的dal屬性使用了[Dependency] Attribute,使得該屬性是通過IoC容器自動去賦值,不需要通過代碼給它顯示賦值。UI的ShowData方法用了[InjectionMethod] Attribute,該方法在UI類被IoC容器自動執行。實際上上面代碼設計到IoC裡面三種方式的其中兩種屬性注入和接口(方法)注入,那麼還剩下一種就是構造器注入,
1 <configuration> 2 <configSections> 3 <section name="unity" 4 type=" 5 Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, 6 Microsoft.Practices.Unity.Configuration"/> 7 </configSections> 8 <unity> 9 <containers> 10 <container name="defaultContainer"> 11 <register type="AllTypeTestControl.IDAL,AllTypeTestControl" mapTo="AllTypeTestControl.DAL,AllTypeTestControl"/> 12 <register type="AllTypeTestControl.IBLL,AllTypeTestControl" mapTo="AllTypeTestControl.BLL,AllTypeTestControl"/> 13 <register type="AllTypeTestControl.IUI,AllTypeTestControl" mapTo="AllTypeTestControl.UI,AllTypeTestControl"/> 14 </container> 15 </containers> 16 </unity> 17 </configuration>
上面這段內容是指定了給Unity的IoC容器各個接口與實現類的映射關系,AllTypeTestControl.IDAL類型映射到AllTypeTestControl.DAL中去,表示通過DAL來實現IDAL的注入,其他同理。但每一項的注冊要按照
<register type="接口全名,程序集名" mapTo="類全名,程序集名"/>
通過下面的測試代碼來測試
1 public static void TestMain() 2 { 3 IUnityContainer container = new UnityContainer(); 4 UnityConfigurationSection configuration = 5 ConfigurationManager.GetSection(UnityConfigurationSection.SectionName) 6 as UnityConfigurationSection; 7 configuration.Configure(container, "defaultContainer"); 8 UI ui = container.Resolve<IUI>() as UI ; 9 }
主要是構造了一個IoC的容器,然後載入了配置文件的映射信息,除了通過配置文件來確定映射關系外,還可以通過代碼的形式來確定
public static void TestMain() { IUnityContainer container = new UnityContainer(); container.RegisterType<IDAL, DAL>(); container.RegisterType<IBLL, BLL>(); container.RegisterType<IUI, UI>(); UI ui = container.Resolve<IUI>() as UI ; }
代碼的運行結果如下
現在則把上面的BLL作一下修改
1 class BLL : IBLL 2 { 3 //[Dependency] 4 //public IDAL dal { get; set; } 5 6 private IDAL dal; 7 8 //[InjectionConstructor] 9 public BLL(IDAL dal) 10 { 11 this.dal = dal; 12 } 13 14 //[InjectionConstructor] 15 public BLL() 16 { 17 18 } 19 20 public List<object> GetSomeDatas() 21 { 22 List<object> result = new List<object>(); 23 DataTable table = dal.QueryDatas(); 24 foreach (DataRow row in table.Rows) 25 { 26 result.Add(row[0]); 27 } 28 return result; 29 } 30 }
結果仍然和上面的一樣,IoC容器仍然能正確的匹配出類型構造了DAL對象。那麼如果給BLL()構造函數加了InjectionConstructor Attribute,IoC容器只會去匹對帶了InjectionConstructor Attribute的構造函數,這樣BLL(IDAL dal)構造函數則不會被調用,運行起來就會拋出空引用異常。假如給BLL(IDAL dal)也加上了InjectionConstructor Attribute,那麼它與無參構造函數BLL()屬於同級,IoC則會也調用BLL(IDAL dal)構造函數,dal字段能被正常的賦值。
通過上面的實踐中能感覺到IoC有GOF中的工廠模式思想。用戶在使用著一個對象,但它並不負責對象的構造,把對象的構造移交了給第三方,在IoC中就是IoC容器,在工廠方法裡面則是工廠了。在ASP.NET MVC中也使用了IoC,迷你MVC框架中控制器的構造是通過了一個工廠利用反射機制來構造出來的,而實際的ASP.NET MVC則是使用了IoC。
對IoC的了解還不算多,手上有一份Ninject的源碼,但一直沒看,現在工作忙了,連博客也少寫了,時間得好好分配,要保持學習。以上有什麼說的不對的請指正,有什麼好的建議或意見也請分享,謝謝!