飛翔的起點

          從這里出發(fā)

          導(dǎo)航

          <2025年7月>
          293012345
          6789101112
          13141516171819
          20212223242526
          272829303112
          3456789

          統(tǒng)計

          常用鏈接

          留言簿(5)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          控制反轉(zhuǎn)(IOC)模式

           

           控制反轉(zhuǎn)(IOC)模式(又稱DI:Dependency Injection)就是Inversion of Control,控制反轉(zhuǎn)。在Java開發(fā)中,IoC意味著將你設(shè)計好的類交給系統(tǒng)去控制,而不是在你的類內(nèi)部控制。這稱為控制反轉(zhuǎn)。

                  IoC(Inversion of Control)是近年來興起的一種思想,不僅僅是編程思想。主要是協(xié)調(diào)各組件間相互的依賴關(guān)系,同時大大提高了組件的可移植性,組件的重用機會也變得更多。在傳統(tǒng)的實現(xiàn)中,由程序內(nèi)部代碼來控制程序之間的關(guān)系。我們經(jīng)常使用new關(guān)鍵字來實現(xiàn)兩組鍵間關(guān)系的組合,這種實現(xiàn)的方式會造成組件之間耦合(一個好的設(shè)計,不但要實現(xiàn)代碼重用,還要將組件間關(guān)系解耦)。IoC很好的解決了該問題,它將實現(xiàn)組件間關(guān)系從程序內(nèi)部提到外部容器來管理。也就是說由容器在運行期將組件間的某種依賴關(guān)系動態(tài)的注入組件中??刂瞥绦蜷g關(guān)系的實現(xiàn)交給了外部的容器來完成。即常說的好萊塢原則“Don't call us, we'll call you”。

                Ioc也有稱為DI(Dependecy Injection 依賴注射),由Martin Fowler的一篇《Inversion of Control Containers and the Dependency Injection pattern》提出。


            分離關(guān)注( Separation of Concerns : SOC)是Ioc模式和AOP產(chǎn)生最原始動力,通過功能分解可得到關(guān)注點,這些關(guān)注可以是 組件Components, 方面Aspects或服務(wù)Services。

            從GOF設(shè)計模式中,我們已經(jīng)習(xí)慣一種思維編程方式:Interface Driven Design 接口驅(qū)動,接口驅(qū)動有很多好處,可以提供不同靈活的子類實現(xiàn),增加代碼穩(wěn)定和健壯性等等,但是接口一定是需要實現(xiàn)的,也就是如下語句遲早要執(zhí)行:

            AInterface a = new AInterfaceImp();

            AInterfaceImp是接口AInterface的一個子類,Ioc模式可以延緩接口的實現(xiàn),根據(jù)需要實現(xiàn),有個比喻:接口如同空的模型套,在必要時,需要向模型套注射石膏,這樣才能成為一個模型實體,因此,我們將人為控制接口的實現(xiàn)成為“注射”。

            Ioc英文為 Inversion of Control,即反轉(zhuǎn)模式,這里有著名的好萊塢理論:你呆著別動,到時我會找你。

            其實Ioc模式也是解決調(diào)用者和被調(diào)用者之間的一種關(guān)系,上述AInterface實現(xiàn)語句表明當前是在調(diào)用被調(diào)用者AInterfaceImp,由于被調(diào)用者名稱寫入了調(diào)用者的代碼中,這產(chǎn)生了一個接口實現(xiàn)的原罪:彼此聯(lián)系,調(diào)用者和被調(diào)用者有緊密聯(lián)系,在UML中是用依賴 Dependency 表示。

            但是這種依賴在分離關(guān)注的思維下是不可忍耐的,必須切割,實現(xiàn)調(diào)用者和被調(diào)用者解耦,新的Ioc模式 Dependency Injection 模式由此產(chǎn)生了, Dependency Injection模式是依賴注射的意思,也就是將依賴先剝離,然后在適當時候再注射進入。

          一、Ioc模式(Dependency Injection模式)有三種:

                第一種類型 從JNDI或ServiceManager等獲得被調(diào)用者,這里類似ServiceLocator模式。 1.EJB/j2ee 2. Avalon(Apache的一個復(fù)雜使用不多的項目) 
                第二種類型 使用JavaBeans的setter方法 1. Spring Framework,2.WebWork/XWork 
                第三種類型 在構(gòu)造方法中實現(xiàn)依賴 1. PicoContainer,2. HiveMind

            有過EJB開發(fā)經(jīng)驗的人都知道,每個EJB的調(diào)用都需要通過JNDI尋找到工廠性質(zhì)的Home接口,在我的教程EJB是什么章節(jié)中,我也是從依賴和工廠模式角度來闡述EJB的使用。

            在通常傳統(tǒng)情況下,為了實現(xiàn)調(diào)用者和被調(diào)用者解耦,分離,一般是通過工廠模式實現(xiàn)的,下面將通過比較工廠模式和Ioc模式不同,加深理解Ioc模式。

          二、工廠模式和Ioc

            假設(shè)有兩個類B 和 C:B作為調(diào)用者,C是被調(diào)用者,在B代碼中存在對C的調(diào)用:

           

          java 代碼
          1. public class B{   
          2.    private C comp;    
          3.   ......   
          4. }   

           

            實現(xiàn)comp實例有兩種途徑:單態(tài)工廠模式和Ioc。

          工廠模式實現(xiàn)如下:

          java 代碼
          1. public class B{   
          2.    private C comp;    
          3.   private final static MyFactory myFactory = MyFactory.getInstance();   
          4.   
          5.   public B(){   
          6.     this.comp = myFactory.createInstanceOfC();   
          7.   
          8.   }   
          9.    public void someMethod(){   
          10.     this.comp.sayHello();   
          11.   }    
          12.   ......   
          13. }   

           

          特點:

          每次運行時,MyFactory可根據(jù)XML配置文件中定義的C子類實現(xiàn),通過createInstanceOfC()生成C的具體實例。 
          使用Ioc依賴性注射( Dependency Injection )實現(xiàn)Picocontainer如下,B類如同通常POJO類,如下:

          java 代碼
          1. public class B{   
          2.    private C comp;    
          3.   public B(C comp){   
          4.     this.comp = comp;   
          5.    }   
          6.    public void someMethod(){   
          7.     this.comp.sayHello();   
          8.    }   
          9.   ......   
          10. }   


           

          假設(shè)C接口/類有有一個具體實現(xiàn)CImp類。當客戶端調(diào)用B時,使用下列代碼:

          java 代碼
          1. public class client{   
          2.    public static void main( String[] args ) {   
          3.     DefaultPicoContainer container = new DefaultPicoContainer();   
          4.     container.registerComponentImplementation(CImp.class);   
          5.     container.registerComponentImplementation(B.class);   
          6.     B b = (B) container.getComponentInstance(B.class);   
          7.     b.someMethod();   
          8.    }   
          9. }   


           

            因此,當客戶端調(diào)用B時,分別使用工廠模式和Ioc有不同的特點和區(qū)別:

            主要區(qū)別體現(xiàn)在B類的代碼,如果使用Ioc,在B類代碼中將不需要嵌入任何工廠模式等的代碼,因為這些工廠模式其實還是與C有些間接的聯(lián)系,這樣,使用Ioc徹底解耦了B和C之間的聯(lián)系。

            使用Ioc帶來的代價是:需要在客戶端或其它某處進行B和C之間聯(lián)系的組裝。

            所以,Ioc并沒有消除B和C之間這樣的聯(lián)系,只是轉(zhuǎn)移了這種聯(lián)系。
            這種聯(lián)系轉(zhuǎn)移實際也是一種分離關(guān)注,它的影響巨大,它提供了AOP實現(xiàn)的可能。

          Ioc和AOP
            AOP我們已經(jīng)知道是一種面向切面的編程方式,由于Ioc解放自由了B類,而且可以向B類實現(xiàn)注射C類具體實現(xiàn),如果把B類想像成運行時的橫向動作,無疑注入C類子類就是AOP中的一種Advice,如下圖:

           

            通過下列代碼說明如何使用Picocontainer實現(xiàn)AOP,該例程主要實現(xiàn)是記錄logger功能,通過Picocontainer可以使用簡單一行,使所有的應(yīng)用類的記錄功能激活。

          首先編制一個記錄接口:

          java 代碼
          1. public interface Logging {   
          2.   
          3.   public void enableLogging(Log log);   
          4.   
          5. }  


           

          有一個LogSwitcher類,主要用來激活具體應(yīng)用中的記錄功能:

          java 代碼
          1. import org.apache.commons.logging.Log;   
          2. public class LogSwitcher   
          3. {   
          4.   protected Log m_log;   
          5.   public void enableLogging(Log log) {   
          6.     m_log = log;   
          7.     m_log.info("Logging Enabled");   
          8.   }   
          9. }   

          一般的普通應(yīng)用JavaBeans都可以繼承這個類,假設(shè)PicoUserManager是一個用戶管理類,代碼如下:

          java 代碼
          1. public class PicoUserManager extends LogSwitcher   
          2. {    
          3.   ..... //用戶管理功能   
          4. }   
          5. public class PicoXXXX1Manager extends LogSwitcher   
          6. {    
          7.   
          8.   ..... //業(yè)務(wù)功能   
          9. }   
          10. public class PicoXXXX2Manager extends LogSwitcher   
          11. {    
          12.   
          13.   ..... //業(yè)務(wù)功能   
          14. }  


           

          注意LogSwitcher中Log實例是由外界賦予的,也就是說即將被外界注射進入,下面看看使用Picocontainer是如何注射Log的具體實例的。


          java 代碼
          1. DefaultPicoContainer container = new DefaultPicoContainer();   
          2. container.registerComponentImplementation(PicoUserManager.class);   
          3. container.registerComponentImplementation(PicoXXXX1Manager.class);    
          4. container.registerComponentImplementation(PicoXXXX2Manager.class);   
          5. .....    
          6.   
          7. Logging logging = (Logging) container.getComponentMulticaster();   
          8.   
          9. logging.enableLogging(new SimpleLog("pico"));//激活log  


           

            由上代碼可見,通過使用簡單一行l(wèi)ogging.enableLogging()方法使所有的應(yīng)用類的記錄功能激活。這是不是類似AOP的advice實現(xiàn)?

            總之,使用Ioc模式,可以不管將來具體實現(xiàn),完全在一個抽象層次進行描述和技術(shù)架構(gòu),因此,Ioc模式可以為容器、框架之類的軟件實現(xiàn)提供了具體的實現(xiàn)手段,屬于架構(gòu)技術(shù)中一種重要的模式應(yīng)用。J道的JdonSD框架也使用了Ioc模式。

          參考資料:

          Inversion of Control Containers and the Dependency Injection pattern
          A Brief Introduction to IoC 
          Ioc容器的革命性優(yōu)點
          Java企業(yè)系統(tǒng)架構(gòu)選擇考量
          IOC模式的思考和疑問

          三、IoC的幾種實現(xiàn)類型

          (1)Type1接口注入

          通常做法是利用接口將調(diào)用者與實現(xiàn)者分離。

          java 代碼
          1. public class Sport {   
          2. private InterfaceBall ball; //InterfaceBall是定義的接口   
          3. public void init() {   
          4. //Basketball實現(xiàn)了InterfaceBall接口   
          5. ball = (InterfaceBall) Class.forName("Basketball").newInstance();   
          6. }   
          7. }  


          Sport類在編譯期依賴于InterfaceBall的實現(xiàn),為了將調(diào)用者與實現(xiàn)者分離,我們動態(tài)生成Basketball類并通了強制類型轉(zhuǎn)換為InterfaceBall。Apache Avalon是一個典型的Type1型IoC容器。

          (2)setter方法注入

          在類中暴露setter方法來實現(xiàn)依賴關(guān)系。

          java 代碼
          1. public class Sport {   
          2. private InterfaceBall ball;   
          3. public void setBall(InterfaceBall arg) {   
          4. ball = arg;   
          5. }   
          6. }  


          這種方式對已經(jīng)習(xí)慣了JavaBean的程序員而言,更顯直觀。Spring就是實現(xiàn)了該類型的輕量級容器。

          (3)Type3構(gòu)造子注入

          即通過構(gòu)造方法完成依賴關(guān)系。

          java 代碼
          1. public class Sport {   
          2. private InterfaceBall ball;   
          3. public Sport(InterfaceBall arg) {   
          4. ball = arg;   
          5. }   
          6. }  


          可以看到,通過類的構(gòu)造方法建立依賴關(guān)系。由于Type3在構(gòu)造期就形成了對象的依賴關(guān)系,即存對象的重用變的困難。有些框架需要組件提供一個默認的構(gòu)造方法,此時就體現(xiàn)了Type3的局限性。通常所有的參數(shù)都是通過構(gòu)造方法注入的,當對象間的依賴關(guān)系較多時,構(gòu)造方法就顯的比較復(fù)雜,不利于單元測試。PicoContainer就是實現(xiàn)了Type3依賴注入模式的輕量級容器。

          posted on 2009-04-09 16:40 forgood 閱讀(319) 評論(0)  編輯  收藏 所屬分類: spring


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


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 姜堰市| 邹城市| 赫章县| 鸡泽县| 乡宁县| 丹江口市| 成武县| 姚安县| 界首市| 遂平县| 左贡县| 扶风县| 广河县| 文化| 扶沟县| 什邡市| 连州市| 西平县| 林西县| 阿图什市| 略阳县| 镇原县| 南川市| 尤溪县| 阿克陶县| 阜平县| 东源县| 延庆县| 喀什市| 寿光市| 永年县| 瑞金市| 永昌县| 湘潭市| 奎屯市| 娱乐| 鄂尔多斯市| 商南县| 疏勒县| 晋州市| 宣武区|