之前就聽同事說過依賴注入(dependency injection)、控制反轉(Inversion of Control)。起初聽的是一頭霧水,試着在項目中運用了幾次,總算明白了一些,拋磚引玉,與大家分享一下拙見。
其實依賴注入和控制反轉指的都是同一個事情。什么是依賴注入了???
【個人理解】
以最熟悉的三層架構的項目來說,BLL層依賴DAL層,UI層依賴於BLL層,層層之間緊密聯系。代碼里到處都是new 對象。認識IOC后,發現IOC最大的好處就是解耦了對這種層級之間的依賴關系進。程序本身不在負責對象的創建和維護,而交給外部容器(IOC容器)來負責。外部容器在運行時動態地將依賴的對象注入到組件之中。
簡單的來說就是在類型A中需要使用類型B的實例,而B實例的創建並不由A來負責,而是通過外部容器來創建。相比以往實例化對象的寫法,確實很爽。
以往實例化都是這樣的:
public class A { public A(B b) { this.B = b; } public B B { get; set; } public void Test(B b) { Console.WriteLine(b.ToString()); } }
A 類受B類的影響很大。A類的構造函數中,實例化B,且在A類的Test的方法中,需要判斷B類是不是被實例化。A即創建B又需要維護B。用了IOC,解耦了這種依賴關系。接下來看看我在項目中是怎么簡單應用的。
【項目簡單試用】
一開始用的IOC容器是Unity,四個字,短小精干(用了有段日子。還有好多功能還需不斷去探索) 。網上還有其它的IOC容器,沒有去了解過(個人認為,能把一個工具用會,用熟,用精,才是王道。)
我把項目中的一段代碼摘出來,項目需求大致上是項目有個數據統計,它下面有三種不同的統計類型,需要與數據庫交互,然后展示到頁面。
首先需要Unity的類庫,利用VS2012的庫程序包管理工具去下載Unity的類庫。
項目的結構是這樣,標准的是三層架構,BLL和DAL都有接口層IBLL,IDAL。
首先我們按照不用IOC的方式來實現這個小需求。
IDAL,IBLL 創建接口 IAnalyse 接口里有個方法
public interface IAnalyse { /// <summary> /// 顯示結果 /// </summary> void ShowResult(); }
DAL 層引用IDAL,創建類 Analyse.cs
public class Analyse:IDAL.IAnalyse { public void ShowResult() { Console.WriteLine("分析底層數據庫交互"); } }
BLL層引用IDAL,DAL,IBLL 創建 類 Analyse.cs
/// <summary> /// 顯示結果 /// </summary> public void ShowResult() { ////以前思路 需要引用IBLL,IDAL,DAL IDAL.IAnalyse dal = new DAL.Analyse(); dal.ShowResult(); }
UI層用控制台應用程序來代替。需引用IBLL,BLL
class Program { static void Main(string[] args) { OldMethod(); Console.ReadKey(); } /// <summary> /// 在使用IOC前的寫法。需要引入IBLL,BLL /// </summary> static void OldMethod() { IBLL.IAnalyse bll = new BLL.Analyse(); bll.ShowResult(); } }
按照以前的方法,每個層我們都在不斷的new 對象。寫到這里發現這些都是很中規中矩的寫法,以前老師教的也是這樣,可能你會覺得沒啥不好的。我就假如:BLL.Analyse 的構造函數需要參數,參數為Class B ,去掉無參的構造函數,你是不是所有new 對象的地方都要更改。小項目也許還沒有問題,如果是上百萬的大項目咋辦。這就是所謂的層與層之間的耦合度高。(個人拙見,多多指教)
接下來我們用 IOC 來解耦。在Program.cs 增加 IOCMethod1 方法
static void Main(string[] args) { //OldMethod(); IOCMethod1(); Console.ReadKey(); } static void IOCMethod1() { IUnityContainer container=new UnityContainer(); ////在容器中注冊一種類型,它是一個類型的映射,接口類型是IAnalyse,希望返回的類型是Analyse container.RegisterType<IBLL.IAnalyse, BLL.Analyse>(); ////第二種寫法 //container.RegisterType(typeof (IBLL.IAnalyse), typeof (BLL.Analyse)); IBLL.IAnalyse bll = container.Resolve<IBLL.IAnalyse>(); bll.ShowResult(); }
使用unity就三步:
第一,new IOC 容器
第二,調用RegisterType 注冊類型。這里有多種注冊形式。可以注冊單例的、構造函數有參數的。
第三,調用Resolve 創建對象
(認識IOC 簡單吧 O(∩_∩)O)
到這,你會發現,UI層 仍然引用了IBLL,BLL,RegisterType方法需要這兩個類庫。既然是解耦,就得解耦徹底些。有什么方法???
我在Core層 DependencyRegister.cs. 建立一個靜態方法,在程序運行開始的時候,注冊所有的類型。UI 層移除BLL ,引用Core
namespace Core { /// <summary> /// 類型注冊 /// </summary> public class DependencyRegister { public static IUnityContainer DependencyRegisterContainer() { IUnityContainer container = new UnityContainer(); container.RegisterType<IBLL.IAnalyse, BLL.Analyse>() .RegisterType<IDAL.IAnalyse, DAL.Analyse>(); return container; } } } ////UI層 先移除BLL,引用Core static void Main(string[] args) { IOCMethod2(); Console.ReadKey(); } static void IOCMethod2() {
IUnityContainer container = DependencyRegister.DependencyRegisterContainer();
IBLL.IAnalyse bll = container.Resolve<IBLL.IAnalyse>();
bll.ShowResult();
}
這樣達到了解耦的目標,如何需要更改類的構造函數,只需更改Core DependencyRegister.cs 就可以了。
【其它解耦方式】
建立core是用了自己得方法來實現解耦的,其實unity還有更覺得,通過配置文件 app.config 來實現。用的是 Microsoft.Practices.Unity.Configuration.dll
<!-- 程序集--> <assembly name="IBLL"/> <assembly name="IDAL"/> <!--要返回的類型--> <alias alias="BLLAnalyse1" type="BLL.Analyse, BLL" /> <container name="ContainerAnalyse"> <register type="IBLL.IAnalyse" name="BLLAnalyse1" mapTo="BLLAnalyse1" /> </container>
private static void IOCMethod3() { ////通過配置文件注冊所有類型 IUnityContainer container=new UnityContainer(); UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity"); section.Configure(container, "ContainerAnalyse"); IBLL.IAnalyse bll = container.Resolve<IBLL.IAnalyse>("BLLAnalyse1"); bll.ShowResult(); }
【投機取巧】
無論是使用全局方法,或者 配置文件,都是注冊類的形式不同。每種方法都需要調用Resolve 來創建對象。有沒有連改方法都不用調用的形式。
不用多說,直接上代碼:
namespace BLL { public class Analyse:IBLL.IAnalyse { ////使用依賴注入 [Dependency] public IDAL.IAnalyse dal { get; set; } /// <summary> /// 顯示結果 /// </summary> public void ShowResult() { ////以前思路 需要引用IBLL,IDAL,DAL //IDAL.IAnalyse dal = new DAL.Analyse(); dal.ShowResult(); } } }
使用[Dependency]屬性。說實話,它的用法還有些沒有弄明白。希望明白這個得多指教指教。
獻丑了,有什么不對的地方希望大家多多指教。Unity 功能很多,這篇只不過是鳳毛麟角,還有什么構造注入,屬性注入、單例的應用。這些,將在下篇繼續分享。希望大家繼續關注,多多指教。