依賴注入與Service Locator

來源:互聯網
上載者:User

標籤:style   class   blog   code   http   tar   

  • 為什麼需要依賴注入?
    • 普通的ServiceUser來負責直接建立所需Service實現的執行個體方法.擁有以下的局限
      • 在不同的環境下,ServiceProvider是千差萬別的(資料庫,臨時檔案,記憶體).
      • 所以,不能將ServiceUser作為組件發布(適應不了各種差異環境).
    • 為了將ServiceUser所在單元作為組件發布,必須滿足以下的條件.
      • 將ServiceUser與具體的ServiceProvider_Imp解耦(解除編譯時間依賴).即不能出現new ServiceProvider_Imp()語句.
      • 在運行時,根據環境來為ServiceUser來動態地提供ServiceProvider的實現執行個體.
      • 總之,將ServiceUser和ServiceProvider之間的依賴,由編譯時間,延遲到運行時動態決定.
      • 即在運行時"注入"這種依賴,稱為依賴注入(DI).
      • ServiceUser可以看做架構.其需要對外發布,被其他模組使用.
      • ServiceProvider可看做外掛程式.其在不同的環境下有差別.
    • 依賴注入
      • 在編譯時間,使用IServiceProvider介面.在運行時,再將具體的實作類別型綁定到ServiceUser上.
      • 從而實現了服務的使用者(架構)和服務的提供者(外掛程式)的松耦合.
    • 加入一個Assembler(容器)來完成對架構單獨發布所需的要求.
      •  
  • 構造器DI
    • 構造器指的是ServiceUser的構造器,也就是在構造ServiceUser的執行個體時,才把具體的ServiceProvider_實現傳遞給它.
    •  1  class ServiceUser 2     { 3         IServiceProvider sp; 4         ServiceUser(IServiceProver sp) 5         { 6             this.sp = sp; 7         } 8     } 9 10     private MutablePicoContainer configureContainer() {11         MutablePicoContainer pico = new DefaultPicoContainer();12         //下面就是把ServiceProvider和ServiceUser都放入容器的過程,以後就由容器來提供ServiceUser的已完成依賴注入執行個體,13         //其中用到的執行個體參數和型別參數一般是從配置檔中讀取的,這裡是個簡單的寫法。14         Parameter[] finderParams =  {new ConstantParameter("movies1.txt")};15         pico.registerComponentImplementation(IServiceProvider.class, ServiceProvider.class, finderParams);16         pico.registerComponentImplementation(ServiceUser.class);        17         //至此,容器裡面裝入了兩個類型,其中沒給出構造參數的那一個(ServiceUser)將依靠其在構造器中定義的傳入參數類型,在容器中18         //進行尋找,找到一個類型匹配項即可進行構造初始化。19         return pico; 20     }
      CtorDI

       

    • 依賴被延遲到容器的構造過程中.
    • 容器本身(具體來說是其構造過程)對ServiceUser和ServiceProvider都有依賴.
    • 所以,ServiceUser,ServiceProvider,容器三者,沒有任何的依賴關係.
    • 所有的依賴關係,所有的變化,都被封裝到了容器的構造中.
    • 執行個體項目中,可以使用設定檔來將變化排斥到編譯期外.
    • 容器含有一個類似GetInstance(Type t)的方法,在該方法中,容器調用ServiceUser的構造器,而使用的參數是在容器添加ServiceProvider時指定的(也就能夠在不同的環境下,使用不同的ServiceProvider的實現).
  • 屬性DI
    • 其與構造器DI的差別
      • 在擷取對象執行個體時(GetInstance方法),前者通過反射得到待建立類型(ServiceUser)的構造器資訊,然後根據構造器的參數類型(ServiceProvider)在容器中進行尋找,然後構造出合適的執行個體.
      • 而屬性DI是通過反射得到待建立類型的所有屬性,然後根據屬性的類型在容器中尋找對應類型的執行個體.
      • 這種方式利於使用XML配置的方式來實現,所以諸如Spring的架構使用的都是這種方式.
  • 介面DI
    • 首先,在介面中定義需要注入的資訊.
    • 然後,在ServiceUser中實現該介面.
    • 最後,由容器調用介面定義的注入方法來完成注入.
  • Service Locator
    • 基本思想:有一個對象(服務定位器)知道如何擷取一個應用程式所需的所有服務(對象).
    • 與DI的區別
      • ServiceUser必須顯示調用Service Locator的方法來擷取對應的服務物件(ServiceProvider)執行個體.
      • 而在DI中,這個過程是由容器隱式完成的.
    • 由於ServiceUser和Servive Locator之間的依賴性,降低了模組之間的獨立性,所以IOC架構大多使用的是DI.
    • 而Locator的優勢是實現簡單,所以當開發工作不複雜時,可以使用Service Locator.
  • DI和Locator的對比
    • 首先,兩者的目的都是解耦ServiceUser和ServiceProvider的實現.
    • 核心區別在於,ServiceProvider的實現,以什麼方式提供給ServiceUser.
      • 在Locator中,ServiceUser直接發送方法調用來請求具體的ServiceProvider實現的執行個體.
      • 在DI中,ServiceUser不再需要任何的調用,由容器來隱式地完成將ServiceProvider的實現執行個體注入到ServiceUser中的過程.
    • 控制反轉(IOC)是架構的基本特徵.
      • 增加了架構代碼的理解難度,同時增加了調試難度.
    • 在選擇兩者的取捨時,主要考慮的是(ServiceUser)對Locator的依賴會不會造成問題.
      • 首先,取決於ServiceUser的性質,如果有多個ServiceUser要使用同一服務,那麼可以使用Locator.
      • 而如果要將ServiceUser作為組件發布供別人使用,而別人的Locator是不可預測的,很可能帶來不相容問題,所以要使用DI.
      • 另一方面, 在DI中,容器和ServiceProvider的實現沒有依賴關係.除了設定檔資訊,服務實現無法擷取更多的關於容器的資訊.
    • 使用DI模式,可以更清晰地理清組件間的依賴關係.
      • 只需要觀察依賴注入機制(例如建構函式).就可以看到整個的依賴關係.
    • 而使用Locator模式,必須在原始碼中搜尋所有對Locator的調用才可以.
    • 測試
      • 部分人會認為DI簡化了測試,因為可以非常簡單地在真實和偽組件(ServiceProvider的實現)之間切換.
      • 但是,良好的Locator實現,應該能做到很容易地替換掉Locator.
      • 也就是說,良好的架構應該支援簡單地使用一個偽組件來替換掉一個真實組件,來方便地進行測試.
      • 所以,如果ServiceProvider的實現如果要脫離自己的控制,在另一個環境中使用,那麼就不能對Locator做任何的假定.
  • 構造DI對比屬性DI
    • 基準:應該在那裡填充欄位(屬性)的值?建構函式還是設定方法?
    • 建構函式的一個好處是隱藏不可變的欄位.
      • 在構造器內將其設定,然後不提供Setter即可.
    • 構造DI的問題
      • 參數個數過多
      • 參數多是無法描述資訊的簡單類型.
      • 對象含有多個建構函式,且有繼承關係.
  • 代碼配置對比設定檔
    • 在簡單的應用中,直接使用代碼來完成配置.
    • 當設定檔變得越發複雜時,使用一種程式設計語言來編寫設定檔.
  • 分離配置和使用
    • 關鍵:服務的配置和使用應該被分離開.
    • 實際上,這反映了一個基本的設計原則:分離介面與實現.
    • 在物件導向的程式中,我們在一個地方根據條件來決定具體執行個體化哪一個子類,之後使用多態(介面類型)來操作該類型.
  • DI的目的
    • 用來降低主程式與對象間的關聯.
    • 同時也能減低對象間的關聯.
    • 簡化對象的建立動作,進而讓對象更加容易使用.

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.