依賴注入實踐篇

來源:互聯網
上載者:User
文章目錄
  • 1.1 依賴關係
  • 1.2 面向介面編程
  • 2.2 IoC容器依賴 
目錄 

1. IoC使用簡介與原理

  1.1 依賴關係

  1.2 面向介面編程

  1.3 IoC使用與實現原理

2. 模式與經驗

  2.1 code configuration vs. xml configuration

  2.2 IoC容器依賴

1. IoC使用簡介與原理
1.1 依賴關係

在物件導向編程中,類與類之間總是要有一些依賴關係,有些依賴強一些,有些依賴弱一些,常見的依賴關係有:

o 繼承依賴 - 子類依賴於父類
o 實現依賴 - 類依賴於介面
o 參數依賴 - 當類的一個方法有其它類型的參數時
o 其它 - 當類中的欄位為其它類型,或者代碼中有對其它類型的使用。

其中參數依賴是相對較弱的依賴關係,而繼承依賴則是較強的依賴關係。判斷兩個類是否有依賴關係有一個很簡單的辦法,比如想知道類A是否依賴於類B,只要把B刪掉然後編譯一下,如果編譯錯誤就證明A依賴於B -- 當然,刪掉B之前代碼應該是編譯正確的。

1.2 面向介面編程

看一下這段代碼:

Code
public class A
{
    private B b;
    public A(B b)
    {
        this.b = b;
    }
    public void Do()
    {
        b.DoSomething();
    }
}
public class B
{
    public void DoSomething()
    {
        //do something
    }
}

其中,A對B有依賴關係,因為B是一個具體類,如果需要添加或改變B的行為則需要直接改變B的代碼,同時B和A都需要重新編譯,這就違反了開放封閉原則。我們可以修改一下代碼,讓A依賴於一個介面IFoo,而B則實現這個介面。這樣A與B就沒有依賴關係了,他們都只依賴於IFoo。這時只需要添加一個實現IFoo的類,再把類的執行個體傳給A就可以改變IFoo的實現和行為。代碼如下:

Code
public class A
{
    private IFoo foo;
    public A(IFoo foo)
    {
        this.foo = foo;
    }
    public void Do()
    {
        foo.DoSomething();
    }
}

public interface IFoo
{
    void DoSomething();
}
    
public class B : IFoo
{
    public void DoSomething()
    {
        //do something
    }
}1.3 IoC使用與實現原理

當我們需要使用A的時候,就需要執行個體化A,最簡單的方式是直接用new

A a = new A(new B());

另一種方式是使用IoC

IUnityContainer myContainer = new UnityContainer();
myContainer.RegisterType<IFoo, B>();
IFoo foo = myContainer.Resolve<IFoo>();
A a = new A(foo);

這裡調用了Unity的兩個主要方法,一個是RegisterType<IService, Service>,用來註冊一個服務和他的實現;另一個是Resolve<IService>,用來得到實現IService的類的執行個體。當我調用myContainer.Resolve<IFoo>()的時候,Unity會發現IFoo是一個介面,那麼Unity就會尋找他的實作類別,也就是B因為我們已經註冊了,接著Unity會使用類似Activator.CreateInstance這樣的方法來執行個體化B,並返回這個執行個體。

由於IoC一般都支援嵌套的依賴關係,因此我們完全不必自己來執行個體化B,可以由容器為我們自動解決。

IUnityContainer myContainer = new UnityContainer();
myContainer.RegisterType<IFoo, B>();
A a = myContainer.Resolve<A>();

在這裡,我們直接向容器請求A的執行個體。大概的步驟如下:

1. 由於A是一個具體類,因此不需要尋找他的實作類別,直接執行個體化A就可以了

2. 查看A的依賴,發現建構函式中有對IFoo的依賴,因此執行個體化A之前,需要先執行個體化IFoo

3. 由於IFoo是一個介面,因此需要尋找他的實作類別,由於我們註冊了B為IFoo的實作類別,因此會執行個體化B

4. 由於B是一個具體類,而且沒有對其它類的依賴關係,因此直接執行個體化B

5. A的所有依賴都得到了執行個體化,將B的執行個體傳給A的建構函式來執行個體化A

6. 返回A的執行個體

可以看到,IoC的執行個體化過程是一個遞迴的過程。每個類所依賴的類或介面都需要首先執行個體化,而這些類或介面又有可能依賴其它的類或介面,這樣一直遞迴下去。假設有一個類C,依賴類D,而類D又依賴於E……最後Y依賴於Z。那麼如果我向IoC容器要一個C的執行個體,IoC會先執行個體化Z,然後是Y,然後是X……E,D最後才是C。

由於IoC會自動解析介面與實作類別的關係,並執行個體化正確的類,因此程式的代碼完全不需要考慮介面的執行個體化問題,可以完全面向介面。而在IoC中註冊類時,也可以通過註冊不同的類,改變介面的行為。

例如,如果我們還有一個IFoo的實作類別C。我們想用C來代替當前的IFoo實現B,只需要修改一行代碼

myContainer.RegisterType<IFoo, B>();
替換成
myContainer.RegisterType<IFoo, C>();

這樣,A就會使用C的實現了。

除了建構函式依賴,Unity也支援其它的依賴,詳細的API和說明請參考文檔。

PS: 我認為Unity有一個比Castle好的特性,就是執行個體化一個具體類時,不需要註冊。如果用Castle的話,上面的樣本還要加上類似於RegisterType<B>()這樣的代碼。

2.模式與經驗
2.1 code configuration vs. xml configuration 

目前的IoC容器一般都會支援代碼配置和Xml配置兩種配置,代碼配置就是向前面的樣本那樣,在代碼中註冊服務介面與實現,而Xml配置則是在Xml或者.config檔案中配置。由於從.NET誕生的那一天起,.config檔案就是一個推薦的配置地點,因此很多人也喜歡將配置資訊放到.config檔案中去,然而代碼配置的方式也有其優勢,在這裡我將比較一下兩種方式的優劣。

在我看來,代碼配置的方式使用起來非常簡單,所需要的程式碼數比較少,而且有自動完成,強型別,編譯時間檢查。唯一的缺點是改變更配置置時,需要重新編譯代碼,而且必須有原始碼才能修改。而xml配置的方式稍微複雜,所需的行數要多一些,沒有編譯時間檢查,需要啟動並執行時候才能直到是否有錯。優點是改配置不需要原始碼,不需要重新編譯,甚至可以在運行時修改。這兩種配置方式的表達能力,在常用情況中幾乎是等價的(代碼配置好像要多一些)。

以我個人的經驗,推薦在開發時使用代碼配置,而在快發布時再改為xml配置。因為開發時,一切都是不確定的,代碼配置的方式修改比較簡單,而且還支援重構。想象一下,如果用xml配置,你用重構工具重新命名了一個介面名,還要到xml中手動修改,這是多大的工作量啊。而在臨近發布時,代碼趨於穩定,修改的可能性很低,這時再放到xml中就可以充分利用xml的好處了。

2.2 IoC容器依賴 

我看到過有些人使用IoC的方式就是把new換成了Resolve而已,幾乎每個類中需要new一個執行個體的時候,都會調一下容器的API。雖然解除了程式中類與類之間的依賴,但是這些類卻都有對IoC容器的依賴。假如我現在使用的容器是Unity,而某一天我發現Unity滿足不了我的需要,我要換一個容器時,由於代碼中遍布著對Unity的直接依賴代碼,替換容器的代價就會非常大。

這裡有兩個解決辦法:

第一就是在程式碼和容器之間加一個層,你可以自己寫一個ServiceLocator之類的類,來封裝IoC容器。這樣程式碼對IoC容器的依賴就轉變成對ServiceLocator的依賴,如果想換一個容器的話,只要修改ServiceLocator的代碼,調用另一種容器的API就行了,減少了很多工作量。由於現在多數IoC的能力和API是比較相似的,因此CodePlex上有一個項目叫CommonServiceLocator,它提供了一個公用的介面,作為這個中介層。但是我本人不推薦使用這個庫,因為本身IoC的介面一點也不複雜,自己寫一個都比學習這個要簡單,還可以根據需要調整介面。

第二種辦法就是減少對IoC容器的依賴,只在最高層代碼或者初始化代碼中調用容器。通常在一個應用程式中,都有一個入口,一般來說是Program類的Main方法,如果你用的是Winform或者WPF這樣的架構,那麼可能會有一個Initialize方法或事件,用於初始化你自己的代碼。如果你的類層次設計良好,你會有一個最高層的類,這個類依賴於同層或底層的一些類,他依賴的類又依賴於更低層的類,而這個類本身卻不被任何類所依賴。那麼當你用容器建立這個類的執行個體時,相當於所有的類都會建立出來。還記得前面的遞迴建立嗎?這種方法適用於那些不需要消極式載入的程式,因為程式啟動時就會建立所有類執行個體。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.