隨筆-159  評論-114  文章-7  trackbacks-0

          原文:http://today.java.net/pub/a/today/2004/02/10/ioc.html

          此文章意在介紹反轉(zhuǎn)模式(Inversion Of Control)的概念以及它是如何簡化并更有效率的進(jìn)行應(yīng)用程序的設(shè)計(jì)。我們將察看IoC框架的不同類型。通過展示IoC能夠帶來更簡單,更靈活的代碼,你也將能夠看到為什么IoC吸引了這么多的興趣。

          IoC理論

          描述IoC是什么和它能提供什么的最佳方式就是看一些簡單的例子。下面的JDBCDataManager類是用于管理應(yīng)用程序?qū)τ跀?shù)據(jù)庫的訪問。這個應(yīng)用程序使用未加工的JDBC最為持久層方案。為了通過JDBC訪問持久層,JDBCDataManager需要一個DataSource對象。標(biāo)準(zhǔn)途徑是硬編碼DataSource對象到類中,像這樣:

          public class JDBCDataManager {

                 public void accessData() {

                        DataSource dataSource = new DataSource();

                        //access data

                 

                 }

          }

          JDBCDataManager正在處理著我們應(yīng)用程序中所有的數(shù)據(jù)訪問,硬編碼DataSource不是很壞,但是我們可能想更抽象DataSource,或許通過某種系統(tǒng)屬性對象得到它:

          public class JDBCDataManager {

                 public void accessData() {

                        DataSource dataSource = ApplicationResources.getDataSource();

          }

          }

          在任意一種情況下,JDBCDataManager不得不自己去得到DataSource

          IoC采取了不同的方式 - 使用IoCJDBCDataManager將聲明它對于一個DataSource的需要,IoC框架會來提供一個。這意味著組件將不再需要知道如何得到依賴的東西,也就帶來了更簡潔,更聚焦,更靈活的代碼。

          Ioc框架

          IoC背后的思想不是很新;實(shí)際上,IoC只是依賴注射原Dependency Inversion Principle 的新縮寫。對于IoC的興趣是它的新東西,還有就是大量框架開始進(jìn)行實(shí)際開發(fā)來輔助IoC的使用。

          IoC框架也是IoC模式的催化物 - 框架的工作,就是在IoC系統(tǒng)中連接組建的粘合劑。當(dāng)IoC的普遍原理被普遍接受時,在眾多框架中就開始有著明確的幾種各自不同的實(shí)現(xiàn)。PicoContainer的開發(fā)者最初定義了三種IoC的類型,是為了區(qū)分當(dāng)時其他框架的實(shí)現(xiàn)途徑。首先,這些類型被簡單的叫做Type123,但是Martin Fowler文章中,Inversion of Control Containers and the Dependency Injection Pattern 他對于這三種類型指定了更有含義的術(shù)語,這些我們將在在下面使用。

          在文章的剩下部分,我們簡要的查看Avalon,然后更加深入討論兩種流行的IoC框架,SpringPicoContainer,他們都提供的IoC類型。

          注射接口(類型1)Interface Injection

          使用IoC注射接口,組件需要實(shí)現(xiàn)容器提供的特殊接口以便被配置。讓我們察看一下使用Avalon框架對于JDBCDataManager的重構(gòu):

          import org.apache.avalon.framework.*;

           

            public class JDBCDataManger implements Serviceable {

              DataSource dataSource;

              public void service (ServiceManager sm)

                    throws ServiceException {

                dataSource = (DataSource)sm.lookup("dataSource");

              }

             

              public void getData() {

                //use dataSource for something

              }

          }

          這種形式的IoC已經(jīng)廣為應(yīng)用,比IoC這個術(shù)語使用的時間還長 大多數(shù)讀者或許早就使用過這種形式的IoC,例如,當(dāng)使用EJB框架時。你的組件擴(kuò)展和實(shí)現(xiàn)了特定接口,這是他們被框架自身調(diào)用。

          Avalon框架事實(shí)上,已經(jīng)提供了IoC框架很多年了,并沒有像SpringPicoContainer任何一者那樣流行,很可能是由于這種實(shí)現(xiàn)途徑的弊端。實(shí)現(xiàn)特殊接口的條件就是使得代碼感覺臃腫,同時也使你的應(yīng)用與框架耦合起來。下面的兩種IoC提供的好處遠(yuǎn)遠(yuǎn)超過這種IoC形式。

          設(shè)置器注射 Setter InjectionType 2

          使用IoCSetter注射,一些外部元數(shù)據(jù)被用于解決依賴性問題。在Spring中,這種元數(shù)據(jù)采取了XML配置文件的形式。使用這種形式的IoCJDBCDataManager類看起來像普通的bean

          public class JDBCDataManger {

            private DataSource dataSource;

           

            public void setDataManager(DataSource dataSource ){

              this.dataSource = dataSource;

            }

           

            public void getData() {

                //use dataSource for something

            }

          }

          我們的JDBCDataManager組件暴露了它的dataSource屬性來允許Spring設(shè)置它。Spring通過XML配置這樣做。那么,我們首先定義了一個數(shù)據(jù)源bean(可以被多個組件重用)

          <bean id="myDataSource"

            class="org.apache.commons.dbcp.BasicDataSource" >

            <property name="driverClassName">

              <value>com.mydb.jdbc.Driver</value>

            </property>

            <property name="url">

              <value>jdbc:mydb://server:port/mydb</value>

            </property>

            <property name="username">

              <value>root</value>

            </property>

          </bean>

          接下來,我們定義一個管理器(數(shù)據(jù)管理器)的實(shí)例并將數(shù)據(jù)源引用傳遞給它:

          <bean id="dataManager"

            class="example.JDBCDataManger">

            <property name="dataSource">

              <ref bean="myDataSource"/>

            </property>

          </bean>

          在運(yùn)行時,JDBCDataManager類將使用正確的DataSource依賴引用來實(shí)例化,然后我們將能夠通過Spring框架本身來訪問該bean

          這種依賴關(guān)系的定義使得單元測試簡單很多:為假設(shè)的仿制對象簡單的定義一個XML文件,代替正常的XML文件,然后就可以測試了。

          或許Setter注射的最大好處就是應(yīng)用程序代碼不需要以任何方式綁定到容器上,但是這也有弊端 不能馬上清楚地知道JDBCDataManager組件與其他一切的關(guān)系。看起來好像DataSource被神奇般的傳遞給JDBCDataManager,正如依賴管理器在Java代碼以外完成這些事情。另外的缺點(diǎn)是Spring需要getterssetters來它的依賴決定。你必須暴露你或許不想暴露的,潛在地破壞了數(shù)據(jù)封裝原則,至少,使得類的接口比你需要的更復(fù)雜些。Spring的元數(shù)據(jù)被描述到XML中,在普通的Java編譯階段的編譯期不能驗(yàn)證他們,意味著那些壞掉的依賴性情況只能在運(yùn)行期才能被看出。

                

          構(gòu)造函數(shù)注射 Constructor Injection(Type 3)

          構(gòu)造器注射基本上基于好市民”Good Citizen的原理。好市民模式由Joshua Bloch提出的,來描述對象在創(chuàng)建時就完全的設(shè)置好并進(jìn)行了使用需要的驗(yàn)證。實(shí)踐中,這意味著,在實(shí)例化后,對象不需要對他們調(diào)用更多的方法,來保證他們的可用性。也就是說,你能夠確保在你創(chuàng)建了對象后,就可以使用。這從根本上簡化了你的代碼并降低了編寫防御性檢查的代碼,同時你的代碼在整體上更加具有防御性(more defensive)。這樣的代碼也更容易測試。

          使用構(gòu)造器注射,你要注冊一個對象到框架中,通過指定參數(shù)來使用(能夠被框架依次創(chuàng)建)并在此時請求一個實(shí)例。被注冊的組件必須實(shí)現(xiàn)一個構(gòu)造器,這個構(gòu)造器得是能夠用來進(jìn)行注射依賴內(nèi)容的。最近,Spring已經(jīng)開始支持這種IoC形式,但是我們而是去察看PicoContainer,它就是建立在這種原理之上的框架。察看JDBCDataManager,現(xiàn)在使用構(gòu)造器注射框架對其重新編碼。

          public class JDBCDataManger {

            private DataSource dataSource; 

            public JDBCDataManger(DataSource dataSource) {

              this.dataSource = dataSource;

            }

           

            public void getData() {

                //use dataSource for something

            }

          }

          不是使用一個配置文件,PicoContainer而是使用一些舊貌換新顏的Java代碼來將一切粘合在一起:

          //創(chuàng)建一個 datasource bean

          BasicDataSource dataSource = new BasicDataSource();

          dataSource.setDriverClassName(“com.mydb.jdbc.Driver”);

          dataSource.setUrl(“jdbc:mysql://localhost:3306/mydb”);

          dataSource.setUsername(“Bob”);

           

          //創(chuàng)建容器

          MutablePicoContainer pico = new DefaultPicoContainer();

           

          //register components with container

          ConstantParameter dataSourceParam = new ConstantParameter(dataSource);

          String key = “DataManager”;

          Parameter[] params = {dataSourceParam};

          /*

          *  Now each request for a DataManager will instantiate

          *  a JDBCDataManager object with our defined

          *  dataSource object

          */

          pico.registerComponentImplemention(key,JDBCDataManager.class,parems);

          為了獲得JDBCDataManager對象的實(shí)例,我們不得不通過它的key來引用這個類:

          JDBCDataManager dm = (JDBCDataManager)pico.getComponentInstance(key);

          Setter注射的IoC,我們的應(yīng)用程序代碼(除了Pico特定的配置之外),是獨(dú)立于框架本身的,并給你帶來了使用好市民模式的繼承來的優(yōu)勢。另外,PicoContainer只需要一個構(gòu)造函數(shù),我們只需要做很少的準(zhǔn)備工作來使用IoC框架相對于Setter注射IoC

          這種方式的潛在問題在于在使用繼承時,去維護(hù)它的依賴性會變得較復(fù)雜,并且他會引起一些事情當(dāng)你使用構(gòu)造器來完成一些別的事情而并非簡單的實(shí)例化對象時。

           

          IoC實(shí)踐:The Data Access Object Pattern

          目前,我們的JDBCDataManger類使用SQL來取得我們的數(shù)據(jù)。假如我們想要通過Hibernate訪問數(shù)據(jù)或者JDO?我們可以使用一個新類來替換JDBCDataManager的使用,但是更好的方案是使用數(shù)據(jù)訪問對象(DAO)模式。概要來說,DAO模式定義了一種方法使得普通的值對象可以用來進(jìn)行設(shè)置和取得數(shù)據(jù)(考慮普通的JavaBeans),并且通過一個抽象數(shù)據(jù)訪問對象來完成的。(For those of you wishing to learn more on the DAO pattern, Sun's Pattern Catalog is a good place to start.)

          我們存在的JDBCDataManager將保持不變。我們將添加DataManager的接口,它將實(shí)現(xiàn)我們數(shù)據(jù)訪問的方法。出于對爭論的考慮,我們也添加Hibernate的實(shí)現(xiàn),HibernateDataManagerJDBCDataManagerHibernateDataManager都開始數(shù)據(jù)訪問對象。

          假設(shè)我們已經(jīng)使用了IoC,改變?yōu)槭褂?/SPAN>DAO是很簡單的事情。假定我們使用上面的Spring代碼,我們能夠使用Hibernate代替JDBC通過XML配置來由HibernateDataManager處理而不是JDBCDataManager類。在切換DAO實(shí)現(xiàn)的時候,我們的應(yīng)用程序代碼將保持不變,假設(shè)他們是期待DataManager接口而不是一個JDBCDataManager實(shí)現(xiàn)。

          通過使用兩種實(shí)現(xiàn)來使用DAO接口,我們正在合并DAO模式與抽象工廠模式Abstract Factory pattern。效果上,IoC框架承擔(dān)了工廠本身的角色。開發(fā)過程中,使用這樣一個模式使得移動到另外的持久機(jī)制的確很容易,并且它對于你的開發(fā)環(huán)境上做很小的設(shè)置相對于你在發(fā)布環(huán)境上做設(shè)置,大有好處。DataManager的兩種實(shí)現(xiàn)可以是同樣的代碼基準(zhǔn),這樣切換他們是微不足道的。

          Spring對比PicoContainer

          PicoContainerSpring細(xì)微不同在于設(shè)置你的IoC綁定所需的工作。Spring能夠被配置要不需要一個XML配置文件或者直接通過Java,然而PicoContainer需要一個Java的綁定(即使PicoExtras項(xiàng)目提供了XML配置支持)。我迅速的得出結(jié)論XML配置文件開始過渡使用(正如某人最近指出的,所有這些不同的配置文件幾乎開始成為他們自己的一種新的語言),盡管哪種方式更好只是個人胃口問題。假如你需要多種配置(比如,一個對于開發(fā),另外一個對于發(fā)布),一個基于XML的系統(tǒng)可能更好的,這只是對于配置管理。

          兩者都是輕型框架 他們可以與其他框架工作很好并具有不必與他們耦合的添加優(yōu)勢。PicoContainer是目前為止兩者中較小的一個;它著手于IoC框架并不沒有提供支持的類來與外部產(chǎn)品像Hibernate一起工作。值得注意的是Spring不只是一個IoC框架:它還提供了web應(yīng)用和AOP框架,同時還有一些普遍的支持類,這些顯著的增加了庫德大小。個人意見,我發(fā)現(xiàn)Springweb應(yīng)用框架非常靈活。然而,它看起來在配置方面需要更多相比于Struts框架,并且至今還沒有一套豐富的支持類。另外,AOP特色仍舊在開發(fā),但是你或許不期待它成為純的AOP框架例如AspectWerkz或者AspectJ.

          如果你打算使用Spring提供的以外的類,你會發(fā)現(xiàn)它是很好的選擇。如果,當(dāng)然,你樂于自行實(shí)現(xiàn)這些(或者依賴外部項(xiàng)目來提供他們)這時Pico的小竅或許是一個決定的因素。兩者都支持構(gòu)造器IoC注射,但是僅僅Spring支持Setter注射如果你更喜歡setter注射,那么Spring是首先。

          結(jié)論

          希望我已經(jīng)展示了通過在您的應(yīng)用中使用IoC,你可以獲得整潔和更靈活的代碼。SpringPicoContainer能夠簡單的被引入到現(xiàn)有的項(xiàng)目作為正在重構(gòu)工作的一部分,并且他們的引入促進(jìn)了日后的重構(gòu)工作。那些從開頭就采取了IoC的項(xiàng)目將發(fā)現(xiàn)他們的應(yīng)用程序代碼將更容易定義內(nèi)部組件的關(guān)系,并且將可以被更靈活和容易的測試。

          posted on 2005-10-31 15:48 北國狼人的BloG 閱讀(1122) 評論(3)  編輯  收藏 所屬分類: 翻譯Java文章

          評論:
          # re: 對于IoC的概要介紹 2006-06-07 17:05 |
          這么小的字怎么看  回復(fù)  更多評論
            
          # re: 對于IoC的概要介紹 2006-06-07 17:05 |
          這么小的字怎么看  回復(fù)  更多評論
            
          # re: 對于IoC的概要介紹 2006-07-11 17:50 | test111
          查看-〉字體-〉最大  回復(fù)  更多評論
            

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


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 新乡市| 永丰县| 朔州市| 景泰县| 沙雅县| 广宗县| 德格县| 靖边县| 双桥区| 江孜县| 安义县| 永济市| 中宁县| 连州市| 舞钢市| 宁城县| 南丹县| 虹口区| 巴楚县| 梓潼县| 上蔡县| 堆龙德庆县| 临泉县| 玛沁县| 淄博市| 中方县| 泗洪县| 光山县| 富平县| 台中市| 巴马| 通辽市| 彩票| 巴里| 四川省| 自贡市| 衡南县| 安徽省| 大余县| 文安县| 旬邑县|