.NET平臺依賴注入機制及IoC的設(shè)計與實現(xiàn)
.NET平臺依賴注入機制及IoC的設(shè)計與實現(xiàn)
我們設(shè)計的分層架構(gòu),層與層之間應(yīng)該是松散耦合的。因為是單向單一調(diào)用,所以,這里的“松散耦合”實際是指上層類不能具體依賴于下層類,而應(yīng)該依賴于下層提供的一個接口。這樣,上層類不能直接實例化下層中的類,而只持有接口,至于接口所指變量最終究竟是哪一個類,則由依賴注入機制決定。之所以這樣做,是為了實現(xiàn)層與層之間的“可替換”式設(shè)計,例如,現(xiàn)在需要換一種方式實現(xiàn)數(shù)據(jù)訪問層,只要這個實現(xiàn)遵循了前面定義的數(shù)據(jù)訪問層接口,業(yè)務(wù)邏輯層和表示層不需要做任何改動,只需要改一下配置文件系統(tǒng)即可正常運行。另外,基于這種結(jié)構(gòu)的系統(tǒng),還可以實現(xiàn)并行開發(fā)。即不同開發(fā)人員可以專注于自己的層次,只有接口被定義好了,開發(fā)出來的東西可以無縫連接。在J2EE平臺上,主要使用Spring框架實現(xiàn)依賴注入。這里,我們將自己做一個依賴注入容器。依賴注入的理論基礎(chǔ)是Abstract Factory設(shè)計模式,這里結(jié)合具體實例簡單介紹一下。
上圖以數(shù)據(jù)訪問層為例,展示了Abstract Factory模式的應(yīng)用。如圖,現(xiàn)假設(shè)有針對Access和SQLServer兩種數(shù)據(jù)庫的數(shù)據(jù)訪問層,它們都實現(xiàn)了數(shù)據(jù)訪問層接口。每個數(shù)據(jù)訪問層有自己的工廠,所有工廠都實現(xiàn)自IDALFactory接口。而客戶類(這里是業(yè)務(wù)邏輯層類)僅與工廠接口、數(shù)據(jù)訪問層接口耦合,而與具體類無關(guān),這樣,只要通過配置文件確定實例化哪個工廠,可以得到不同的數(shù)據(jù)訪問層。然而,這種設(shè)計雖然可行,但是代碼比較冗余,因為這樣需要為數(shù)據(jù)訪問層的每一個實現(xiàn)編寫一個工廠,業(yè)務(wù)邏輯層也一樣。在以前,我們毫無辦法,但是,.NET平臺引入的反射機制,給我們提供了一種解決方案。使用反射,每個層只需要一個工廠,然后通過從配置文件中讀出程序集的名稱,動態(tài)加載相應(yīng)類。另外,為了提高依賴注入機制的效率,這里引入緩存機制。下面來看具體實現(xiàn)。配置首先,需要在Web工程的Web.config文件的節(jié)點下添加如下兩個項:
這兩個配置選項分別存儲要應(yīng)用的數(shù)據(jù)訪問和也業(yè)務(wù)邏輯層的程序集名稱。value目前是空,是因為目前還沒有各個層次的具體實現(xiàn)。實現(xiàn)緩存操作輔助類為實現(xiàn)緩存操作,我們將緩存操作封裝成一個輔助類,放在Utility工程下,具體代碼如下:
using System;using System.Web;using System.Web.Caching;
namespace NGuestBook.Utility{ /**//// /// 輔助類,用于緩存操作 /// public sealed class CacheAccess { /**//// /// 將對象加入到緩存中 /// /// 緩存鍵 /// 緩存對象 /// 緩存依賴項 public static void SaveToCache(string cacheKey, object cacheObject, CacheDependency dependency) { Cache cache = HttpRuntime.Cache; cache.Insert(cacheKey, cacheObject, dependency); }
/**//// /// 從緩存中取得對象,不存在則返回null /// /// 緩存鍵 /// 獲取的緩存對象 public static object GetFromCache(string cacheKey) { Cache cache = HttpRuntime.Cache;
return cache[cacheKey]; } }}
封裝依賴注入代碼因為很多依賴注入代碼非常相似,為了減少重復(fù)性代碼,我們將可復(fù)用的代碼先封裝在一個類中。具體代碼如下(這個類放在Factory工程下):
using System;using System.Configuration;using System.Reflection;using System.Web;using System.Web.Caching;using NGuestBook.Utility;
namespace NGuestBook.Factory{ /**//// /// 依賴注入提供者 /// 使用反射機制實現(xiàn) /// public sealed class DependencyInjector { /**//// /// 取得數(shù)據(jù)訪問層對象 /// 首先檢查緩存中是否存在,如果不存在,則利用反射機制返回對象 /// /// 數(shù)據(jù)訪問類名稱 /// 數(shù)據(jù)訪問層對象 public static object GetDALObject(string className) { /**//// /// 取得數(shù)據(jù)訪問層名稱,首先檢查緩存,不存在則到配置文件中讀取 /// 緩存依賴項為Web.Config文件 /// object dal = CacheAccess.GetFromCache("DAL"); if (dal == null) { CacheDependency fileDependency = new CacheDependency(HttpContext.Current.Server.MapPath("Web.Config")); dal = ConfigurationManager.AppSettings["DAL"]; CacheAccess.SaveToCache("DAL", dal, fileDependency); }
/**//// /// 取得數(shù)據(jù)訪問層對象 /// string dalName = (string)dal; string fullClassName = dalName + "." + className; object dalObject = CacheAccess.GetFromCache(className); if (dalObject == null) { CacheDependency fileDependency = new CacheDependency(HttpContext.Current.Server.MapPath("Web.Config")); dalObject = Assembly.Load(dalName).CreateInstance(fullClassName); CacheAccess.SaveToCache(className, dalObject, fileDependency); }
return dalObject; }
/**//// /// 取得業(yè)務(wù)邏輯層對象 /// 首先檢查緩存中是否存在,如果不存在,則利用反射機制返回對象 /// /// 業(yè)務(wù)邏輯類名稱 /// 業(yè)務(wù)邏輯層對象 public static object GetBLLObject(string className) { /**//// /// 取得業(yè)務(wù)邏輯層名稱,首先檢查緩存,不存在則到配置文件中讀取 /// 緩存依賴項為Web.Config文件 /// object bll = CacheAccess.GetFromCache("BLL"); if (bll == null) { CacheDependency fileDependency = new CacheDependency(HttpContext.Current.Server.MapPath("Web.Config")); bll = ConfigurationManager.AppSettings["BLL"]; CacheAccess.SaveToCache("BLL", bll, fileDependency); }
/**//// /// 取得業(yè)務(wù)邏輯層對象 /// string bllName = (string)bll; string fullClassName = bllName + "." + className; object bllObject = CacheAccess.GetFromCache(className); if (bllObject == null) { CacheDependency fileDependency = new CacheDependency(HttpContext.Current.Server.MapPath("Web.Config")); bllObject = Assembly.Load(bllName).CreateInstance(fullClassName); CacheAccess.SaveToCache(className, bllObject, fileDependency); }
return bllObject; } }}
實現(xiàn)工廠 下面使用兩個輔助類,實現(xiàn)數(shù)據(jù)訪問層工廠和業(yè)務(wù)邏輯層工廠。
using System;using NGuestBook.IDAL;
namespace NGuestBook.Factory{ /**//// /// 數(shù)據(jù)訪問層工廠,用于獲取相應(yīng)的數(shù)據(jù)訪問層對象 /// 使用Abstract Factory設(shè)計模式+Facace設(shè)計模式+反射機制+緩存機制設(shè)計 /// public sealed class DALFactory { /**//// /// 獲取管理員數(shù)據(jù)訪問層對象 /// /// 管理員數(shù)據(jù)訪問層對象 public static IAdminDAL CreateAdminDAL() { return (IAdminDAL)DependencyInjector.GetDALObject("AdminDAL"); } /**//// /// 獲取留言數(shù)據(jù)訪問層對象 /// /// 留言數(shù)據(jù)訪問層對象 public static IMessageDAL CreateMessageDAL() { return (IMessageDAL)DependencyInjector.GetDALObject("MessageDAL"); }
/**//// /// 獲取評論數(shù)據(jù)訪問層對象 /// /// 評論數(shù)據(jù)訪問層對象 public static ICommentDAL CreateCommentDAL() { return (ICommentDAL)DependencyInjector.GetDALObject("CommentDAL"); } }}
using System;using NGuestBook.IBLL;
namespace NGuestBook.Factory{ /**//// /// 業(yè)務(wù)邏輯層工廠,用于獲取相應(yīng)的業(yè)務(wù)邏輯層對象 /// 使用Abstract Factory設(shè)計模式+Facace設(shè)計模式+反射機制+緩存機制設(shè)計 /// public sealed class BLLFactory { /**//// /// 獲取管理員業(yè)務(wù)邏輯層對象 /// /// 管理員業(yè)務(wù)邏輯層對象 public static IAdminBLL CreateAdminBLL() { return (IAdminBLL)DependencyInjector.GetBLLObject("AdminBLL"); }
/**//// /// 獲取留言業(yè)務(wù)邏輯層對象 /// /// 留言業(yè)務(wù)邏輯層對象 public static IMessageBLL CreateMessageBLL() { return (IMessageBLL)DependencyInjector.GetBLLObject("MessageBLL"); }
/**//// /// 獲取評論業(yè)務(wù)邏輯層對象 /// /// 評論業(yè)務(wù)邏輯層對象 public static ICommentBLL CreateCommentBLL() { return (ICommentBLL)DependencyInjector.GetBLLObject("CommentBLL"); } }}