J2EE之巔

           

          依賴注入(Dependency Injection)模式的特點分析與實現(xiàn)

          依賴注入(Dependency Injection模式的特點分析與實現(xiàn)

          ――構(gòu)造子注入Constructor Injection模式的分析與實現(xiàn)

           

           

           

          chaocai2001@yahoo.com.cn

           

          摘要:本文對IoC模式、依賴注入(Dependency Injection) 模式做了簡要介紹,文中分析構(gòu)造子注入模式與其他模式相比較的優(yōu)勢和特點,并給出了在JAVA中實現(xiàn)該模式的方法。

           

           

          1         引言

          IoCInversion of Control)模式以被目前的輕量級容器所廣泛應用,通過IoC模式這些容器幫助開發(fā)者將來自不同項目的組件裝配成一個內(nèi)聚的應用程序。輕量級的IoC容器(如Springpico-container)雖然為我們的開發(fā)提供了很大的便利,但是在很多情況下這些輕量級容器所提供的功能并不一定非常適合我們的需要,也許這些容器的功能過于龐大了,或者所提供的功能缺乏對特定應用的針對性,或者我們需要更高的運行效率,這時我們可以在了解IoC的原理的基礎上利用JAVA的反射機制自己實現(xiàn)靈活的、可擴展的組件機制。

          2         IoC與依賴注入(Dependency Injection)模式簡介

          GoF的設計模式相同,IoC模式同樣是關注重用性,但與GoF模式不同的是IoC模式更加關注二進制級的重用性和可擴展性,即可以直接通過二進制級進行擴充,復用的模塊通常被稱為組件或者插件,組件和插件都是在運行時進行裝載的。

          GoF的設計模式中我們大量看到的是面向接口編程:Interface Driven Design 接口驅(qū)動,接口驅(qū)動有很多好處,可以提供不同靈活的子類實現(xiàn),增加代碼穩(wěn)定和健壯性等等,但是接口一定是需要實現(xiàn)的,也就是如下語句遲早要執(zhí)行:

          AInterface a = new AInterfaceImp();

          由于以上的代碼被寫入了調(diào)用者程序中,同時象AinterfaceImp這樣的接口的實現(xiàn)類是在編譯時被裝載的,如果以后想加入新的接口實現(xiàn)類則必須修改調(diào)用者的代碼。

          IoC模式與以上情況不同,接口的實現(xiàn)類是在運行時被裝載的,這樣即使以后新添加了接口實現(xiàn)類是也不需修改調(diào)用者的代碼(可以通過特定的方式來定位新增的實現(xiàn)類,如配置文件指定)。IoC英文為 Inversion of Control,即反轉(zhuǎn)模式,這里有著名的好萊塢理論:你呆著別動,到時我會找你。

          IoC模式可以延緩接口的實現(xiàn),根據(jù)需要實現(xiàn),有個比喻:接口如同空的模型套,在必要時,需要向模型套注射石膏,這樣才能成為一個模型實體,因此,對于這些新生的容器,它們反轉(zhuǎn)的是“如何定位插件的具體實現(xiàn)”。因此, Martin Fowler 給這種模式起了一個形象的名稱“依賴注入”Dependency Injection。

          圖表 1 采用Dependency Injection前后的依賴關系變化

          依賴注入的形式主要有三種,分別將它們叫做構(gòu)造子注入(Constructor Injection)、設值方法注入(Setter Injection)和接口注入(Interface Injection)。

          這三種方式在Martin Fowler的《Inversion of Control Containers and the Dependency Injection pattern》中都給出了詳細的定義及說明,本文就不再贅述了,下面的內(nèi)容將著重介紹構(gòu)造子注入模式的特點及實現(xiàn)方法。

          3         構(gòu)造子注入模式的特點及實現(xiàn)

          3.1      構(gòu)造子注入模式的特點

          通常情況下設方法注入和接口注入較易于被開發(fā)人員接受,而構(gòu)造子注入則應用較少,實際上構(gòu)造子注入具有很多其他兩者所不具有的優(yōu)勢:

          1 構(gòu)造子注入形成了一種更強的依賴契約

          2 可以獲得更加簡明的代碼

          3 更加簡明的依賴聲明機制,無須定義XML配置文件或設方法

          4 更加符合接口與實現(xiàn)分離的組件特征,組件接口表明能夠向其它組件提供的服務,而實現(xiàn)則應該是所提供服務的實現(xiàn)應該與服務契約無關(即不應包含用于獲得依賴的設值方法等)。

          5 不會出現(xiàn)不確定的狀態(tài)。在設值方法注入中,由于并不是所有的設值方法(setter)都一定會被調(diào)用的,所以會有不確定狀態(tài)。

          從以上幾點我們還可以分析出構(gòu)造子注入對于組件代碼的入侵性遠小于其它兩種模式(接口注入使得組件必須實現(xiàn)特定接口,設值方法同樣要求組件提供特定的setter方法),代碼更加易于維護

          圖表 2 示例中類的關系

          Client的實現(xiàn)依賴于接口ABC的實現(xiàn),但是為了提供系統(tǒng)更好的靈活性和可擴展性,各接口的實現(xiàn)以組件的方式利用java的反射機制進行運行時裝載,注意到組件間可能會存在某種依賴關系,例如組件AX依賴與接口B的實現(xiàn)類,而這中依賴關系必須在運行時動態(tài)注入,組件為了告訴組件的調(diào)用者這種依賴關系以便注入,可以使用上文提到的各種模式:

          1 使用接口注入模式

          public interface InjectB{

                 public void injectB(B bImp);

          }

          public interface InjectC{

                 public void injectC(C cImp);

          }

          public class AImp implements A,InjectB,InjectC{

                

                 public void injectB(B bImp);

                 public void injectC(C cImp);

                

          }

          2 使用設值注入模式

          public class AImp implements A {

                

                 public void setB(B bImp);

                 public void setC(C cImp);

                

          }

           

          3 使用構(gòu)造子注入模式

          public class AImp implements A {

                

                 public AImp(B bImp, C cImp){

                       

                 }

                

          }

          由以上實例可以清楚的看出采用構(gòu)造子注入模式的實現(xiàn)組件代碼最為簡單,且所受的入侵性最小。

          3.2      JAVA中實現(xiàn)構(gòu)造子注入模式

          java.NET這樣具有反射功能的語言中實現(xiàn)類型的運行時載入并不復雜,只要通過Class.forName或生成自己的ClassLoader就可以實現(xiàn)。

          同樣我們可以通過反射機制獲取組件構(gòu)造函數(shù)的參數(shù),注入相應接口的實現(xiàn),作者將此過程進行了封裝,以下是代碼:

          public class RefectHelper {

                   public Object ConstructorHelper(String className,ConstructorParamDeal pd) throws Exception{

                             try{

                                      //獲取類中的構(gòu)造函數(shù)

          Constructor[] constructs=Class.forName(className).getConstructors();              //實現(xiàn)中默認使用第一個構(gòu)造函數(shù)類創(chuàng)建實例      

                                      Class [] classes=constructs[0].getParameterTypes();                  

                                        //獲取要注入的參數(shù)實例

                                      Object []obj=pd.dealParam(classes);

                                      //創(chuàng)建實例

                                      return constructs[0].newInstance(obj);

                             }catch(Exception e){

                                      throw e;

                             }

                            

                            

                   }

          }

          /**

          *構(gòu)造函數(shù)參數(shù)注入

          **/

          public interface ConstructorParamDeal {

                   /**

                   *根據(jù)構(gòu)造函數(shù)中參數(shù)的類型注入,相應的實現(xiàn)

                   @param classes 構(gòu)造函數(shù)的參數(shù)類型

                   return 注入構(gòu)造函數(shù)的參數(shù)實現(xiàn)

                   **/

                   public Object [] dealParam(Class [] classes);

          }

          public class ParamDeal implements ConstructorParamDeal{

           

                   /* (non-Javadoc)

                    * @see com.topsec.tsm.agent.helper.ConstructorParamDeal#dealParam(java.lang.Class[])

                    */

                   public Object [] dealParam(Class[] classes) {

                             Object [] obj=new Object[classes.length];

                             for (int i=0;i<obj.length;i++){

                                      //為不同類型注入選擇不同實例

                                      if (classes[i].equals(String.class)){

                                               obj[i]=”Hello World”;                                       

                                      }

                             }

                             return obj;

                   }

           

          }

          上面的程序中ConstructorHelper用于利用反射機制枚舉出載入類的構(gòu)造函數(shù)及構(gòu)造函數(shù)的參數(shù)的類型,至于不同類型注入什么樣的實例則由ContructorParamDeal的實現(xiàn)者來決定,ContructorParamDeal的實現(xiàn)者同樣可以以組件的形式在運行時動態(tài)載入。由于組件間的依賴關系的制約,所以組件實例化的順序需要特別考慮。

          4         結(jié)束語

          三種依賴注入模式各有其特點和優(yōu)勢,只有充分理解這些模式間的不同,才能為自己的應用選擇正確的依賴注入模式,文中介紹的構(gòu)造子注入模式實現(xiàn)方法,在使用其他具有反射功能的語言(如:.NET)時同樣可以參考。

           

           

           

           [參考文獻]

          1 Martin Fowler,Inversion of Control Containers and the Dependency Injection pattern,http://www.martinfowler.com/articles/injection.html,2004

          2 Erich Gamma,Design Patterns,Addison Wesley,1999

          3 http://www.picocontainer.org/

          4 彭晨陽,Ioc模式, http://www.jdon.com,2004

          posted on 2006-03-14 08:52 超越巔峰 閱讀(7990) 評論(1)  編輯  收藏

          評論

          # re: 依賴注入(Dependency Injection)模式的特點分析與實現(xiàn) 2010-06-22 10:43 愛之谷

          三種依賴注入模式各有其特點和優(yōu)勢,只有充分理解這些模式間的不同,才能為自己的應用選擇正確的依賴注入模式,文中介紹的構(gòu)造子注入模式實現(xiàn)方法,在使用其他具有反射功能的語言(如:.NET)時同樣可以參考。  回復  更多評論   


          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導航:
           

          導航

          統(tǒng)計

          常用鏈接

          留言簿(12)

          隨筆分類(54)

          隨筆檔案(59)

          文章分類(2)

          文章檔案(1)

          相冊

          搜索

          積分與排名

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 金昌市| 淮阳县| 鄂伦春自治旗| 偏关县| 元阳县| 德州市| 宁陕县| 巍山| 台州市| 富川| 临夏市| 临汾市| 买车| 和静县| 剑川县| 田阳县| 郧西县| 兰西县| 高雄市| 古蔺县| 沾化县| 霍邱县| 武邑县| 尼玛县| 江西省| 滦南县| 郴州市| 中阳县| 洪雅县| 临朐县| 砚山县| 永和县| 平邑县| 汉中市| 朝阳市| 卢氏县| 嘉禾县| 英山县| 寿光市| 泗洪县| 滁州市|