Castle學習筆記之Windsor(一)

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:

namespace WindsorTest.Interface
{
    
interface ICacheConfig
    
{
        
string GetCacheKey();
    }

}

ICacheEngine:
namespace WindsorTest.Interface
{
    
public interface ICacheEngine
    
{
        
string GetCacheData(string key);
    }

}

ICacheManager:
namespace WindsorTest.Interface
{
    
interface ICacheManager
    
{
        
string GetData();
    }

}


接下來開始創建接口們的具體實現:

MyConfig

namespace WindsorTest.Components
{
    
class MyConfig : ICacheConfig
    
{

        
public MyConfig()
        
{
        }


        
#region ICacheConfig Members


        
/// <summary>
        
/// 獲取緩存數據的key值
        
/// </summary>
        
/// <returns></returns>

        public string GetCacheKey()
        
{
            
return "key";
        }


        
#endregion

    }

}

MyCacheEngine
namespace WindsorTest.Components
{
    
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
namespace WindsorTest.Services
{
    
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

    }

}

到此,我們的大部分代碼結束了,不過大概許多朋友還沒看出來這與普通的接口編程有什麼區別?呵呵,確實如此,到現在爲止依然與之前的做法沒有兩樣,那是因爲關鍵的調用還沒有開始.通常,我們也許會寫下下面的代碼來結束這個程序.
ICacheManager manger = new MyCacheManager(new MyCacheEngine("key""value"), new MyConfig());
            manager.GetData();

瞧,我們需要三個構造過程,而且我們必須清楚每個構造函數裏面的參數是什麼意思,我們只是想要從緩存裏讀個值,居然要了解這麼多的事??
看看我們的Windsor是如何來解決這個問題的吧.

static void Main(string[] args)
        
{
            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,執行它.看看裏面,沒有一個對象的實例化行爲(除了容器),三個對象間的依賴完全消失了!當然,這個樣子是不能立刻運行的,我們還需要添加一個小小的配置文件.
<?xml version="1.0" encoding="utf-8" ?>
<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容器快速入門

發佈了2 篇原創文章 · 獲贊 0 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章