Castle大名早就如雷貫耳,早就想研究一下,無奈公司項目一直比較緊迫,直到前天BOSS告訴我該轉入下一部分框架的設計工作了,嘿嘿,剛好借這個機會,開始我的Castle之旅:)
首先向不熟悉的朋友介紹下Castle到底是什麼.Castle是.net平臺上的一個開源項目,爲企業級開發和WEB應用程序開發提供完整的服務.當前開發的許多流行元素,比如IOC,ORM都在Castle上有非常成熟的應用.http://www.castleproject.org是其官方站點,你可以在上面瞭解更多有關Castle的信息.點擊此處立刻下載sample和source的完整安裝包.
Windsor是Castle下的一個項目,用於提供IOC的解決方案.IOC被稱爲控制反轉或者依賴注入(Dependency Injection).關於它的嚴格定義和解釋可以參照大師的文章http://martinfowler.com/articles/injection.html . 我的解釋就是,IOC的目的就是用於對象間的解耦,把對象之間的依賴關係延遲注入.
稍有經驗的開發者都已經開始習慣於基於接口或者抽象類的編程,當我們想要創建一個對象時,我們通常會爲其建立一個接口,哪怕從這個接口再派生對象的可能性非常小.這最大的好處當然是高擴展性,我們永遠不知道客戶的需求會怎麼變化,爲了適應需求的變化,我們只能希望每次變化不會帶來過長的鏈式反應和類的爆炸式增長.以我目前的項目爲例子:我需要創建一個緩存管理的程序,按照上頭的意思,將採用MS的企業庫來對緩存進行處理,那麼我首先需要對外暴露一個CacheManger的類,爲簡單起見,我這個類只有一個方法GetData().這個方法並不實際執行什麼操作,它將調用CacheEngine裏的方法來取出具體的緩存的值,但是CacheEngine的方法需要一個額外的參數,key,這個key值我們將從另一個類CacheConfig裏取出.調用很簡單:CacheManger->CacheEngine&CacheConfig.
相信沒有程序員會這麼做,我們一定會建立ICacheEngine和ICacheConfig接口,同時各自派生一個MyCacheEngine和MyCacheConfig類,當需求有變化時,比如我們又被要求以System.Web.Cacheing或者Enterprise Service的方式進行緩存的處理時,我們立刻會從ICacheEngine裏派生新的類,而無須修改之前的類,遵循了開閉原則.
長久以來,很多程序員都這麼做(包括我自己),而且這麼做本身沒有什麼錯.不過我們必須認識到,在這種模式下,對象間的依賴是無法解除的.在這裏,CacheManager裏的GetData方法不僅依賴於ICacheEngine和ICacheConfig接口,同時還依賴於它們的具體實現—MyCacheEngine和MyConfig.這就是問題的所在:對象間的耦合!
這裏我並不極端的認爲要追求絕對的鬆耦合,實際上那也是不現實的,即使是IOC也不過是延遲了對象的關聯,關鍵問題是:我們能否消除此處的耦合關係?
答案是肯定的,當然這裏的消除也是將耦合移到外部,並不是真正的清除.接下來讓我們看看在Windsor裏是如何做到這一點的.
依然是上面的例子,我們先爲其添加需要的接口.
ICacheConfig:
{
interface ICacheConfig
{
string GetCacheKey();
}
}
ICacheEngine:
{
public interface ICacheEngine
{
string GetCacheData(string key);
}
}
ICacheManager:
{
interface ICacheManager
{
string GetData();
}
}
接下來開始創建接口們的具體實現:
MyConfig
{
class MyConfig : ICacheConfig
{
public MyConfig()
{
}
#region ICacheConfig Members
/// <summary>
/// 獲取緩存數據的key值
/// </summary>
/// <returns></returns>
public string GetCacheKey()
{
return "key";
}
#endregion
}
}
MyCacheEngine
{
class MyCacheEngine : ICacheEngine
{
#region ICacheEngine Members
/// <summary>
/// 創建一個CacheEngine實例
/// </summary>
/// <param name="key">key值</param>
/// <param name="value">數值</param>
public MyCacheEngine(string key,string value)
{
CacheManager cm = CacheFactory.GetCacheManager();
cm.Add(key, value);
}
/// <summary>
/// 獲取指定key值的cache
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public string GetCacheData(string key)
{
CacheManager cm = CacheFactory.GetCacheManager();
if (cm.GetData(key) == null)
cm.Add(key, key);
return cm.GetData(key).ToString() ;
}
#endregion
}
}
MyCacheManager
{
class MyCacheManager : ICacheManager
{
#region construct&members
private ICacheEngine _engine;
private ICacheConfig _config;
/// <summary>
/// 構造CacheManager
/// </summary>
/// <param name="engine"></param>
/// <param name="config"></param>
public MyCacheManager(ICacheEngine engine, ICacheConfig config)
{
_engine = engine;
_config = config;
}
#endregion
#region ICacheManager Members
/// <summary>
/// 獲取數據
/// </summary>
/// <returns></returns>
public string GetData()
{
string key = _config.GetCacheKey();
return _engine.GetCacheData(key);
}
#endregion
}
}
到此,我們的大部分代碼結束了,不過大概許多朋友還沒看出來這與普通的接口編程有什麼區別?呵呵,確實如此,到現在爲止依然與之前的做法沒有兩樣,那是因爲關鍵的調用還沒有開始.通常,我們也許會寫下下面的代碼來結束這個程序.
manager.GetData();
瞧,我們需要三個構造過程,而且我們必須清楚每個構造函數裏面的參數是什麼意思,我們只是想要從緩存裏讀個值,居然要了解這麼多的事??
看看我們的Windsor是如何來解決這個問題的吧.
{
IWindsorContainer container = new WindsorContainer(new XmlInterpreter("../../config.xml"));
container.AddComponent("manager", typeof(ICacheManager), typeof(MyCacheManager));
container.AddComponent("config", typeof(ICacheConfig), typeof(MyConfig));
container.AddComponent("engine", typeof(ICacheEngine), typeof(MyCacheEngine));
ICacheManager manager = (ICacheManager)container["manager"];
Console.Write(manager.GetData());
Console.ReadLine();
//ICacheManager manger = new MyCacheManager(new MyCacheEngine("key", "value"), new MyConfig());
//manager.GetData();
}
分析一下這段代碼,首先,我們需要一個IOC容器,並指定配置文件(這個稍後再說).然後挨個註冊我們用到了的三個接口的實現(這裏也有許多的註冊方式,也稍後再說).接下來,我們從容器裏取出我們需要的CacheManager,執行它.看看裏面,沒有一個對象的實例化行爲(除了容器),三個對象間的依賴完全消失了!當然,這個樣子是不能立刻運行的,我們還需要添加一個小小的配置文件.
<configuration>
<components>
<component id="engine">
<parameters>
<key>key</key>
<value>key-value</value>
</parameters>
</component>
</components>
</configuration>
哈,運行下,看看?沒問題,接下來我再來解釋下爲何這裏就能簡單的解決對象間的依賴關係.你可以把每次的註冊看做一次對象的實例化,但是這個過程我們並不需要指定任何參數,而對象的引用也都是智能的實現,當然這裏面有一定的規則在裏面,在下一篇文章裏,我將再來更深入的談談註冊的方式和構造注入是如何實現的.
下班時間到了,回去咯,我會抽時間儘快的開始我的該系列的第二篇^_^.
注:本人也是以學習和研究的態度來寫這篇文章,裏面的許多觀點只代表我的個人思想,加之研究Windsor尚淺,裏面必然有許多不當之處,還請各位看官多多包涵:)
參考文獻:
Introducing Castle
Inversion of Control Containers and the Dependency Injection pattern
Ioc模式
Castle IOC容器快速入門