<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    OOPAA

    Focusing on OO, Patterns, Architecture, and Agile
    posts - 29, comments - 75, trackbacks - 0, articles - 0
      BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

    DIP 沉思錄

    Posted on 2008-11-02 17:37 mingj 閱讀(1850) 評論(2)  編輯  收藏 所屬分類: OO 面向?qū)ο?/a>
    Dependency Injection 這個(gè)名詞,是在 Martin Fowler 的《Inversion of Control Containers and the Dependency Injection pattern》文章之后才廣為人知。在文章中,Martin 解釋了當(dāng)時(shí)初起流行的 IOC 概念:為了消除應(yīng)用程序?qū)Σ寮?shí)現(xiàn)的依賴,程序的主控權(quán)從應(yīng)用程序移到了框架。為了讓 IOC 概念不那么令人迷惑,Martin 把流行的幾種 IOC Container 實(shí)現(xiàn)模式命名為 Dependency Injection Pattern(DIP,下文簡稱 DI 模式)。很明顯,DI 的定義更準(zhǔn)確形象,而且因?yàn)?Martin 在軟件開發(fā)社區(qū)巨大的影響力,DI 模式以及 spring framework 作為其最成功的實(shí)現(xiàn)之一很快被軟件開發(fā)社區(qū)接受并成為日常開發(fā)的必備利器。

    “依賴注入”給我們代碼的編寫,特別是測試代碼的編寫帶來了很多好處。借助于 DI 模式,我們可以將服務(wù)的依賴聲明和具體實(shí)現(xiàn)相分離,通過配置不同的服務(wù)實(shí)例交給框架管理,來讓應(yīng)用程序獲得良好的靈活性和柔性。

    比如我們有這樣的代碼:

    public void hello() {
        Foo foo 
    = new Foo();
        foo.sayHello();
        
    //
    }


    這種情況下,hello() 方法就依賴于 Foo 類的 sayHello() 方法實(shí)現(xiàn),會(huì)給測試帶來了很多的不穩(wěn)定性,比如耗時(shí)、服務(wù)異常之類。如果依照 DI 模式來重構(gòu),這段代碼就會(huì)變成這樣:

    public void hello(Foo foo) {
        foo.sayHello();
        
    //
    }

    在測試時(shí)我們就可以使用行為確定符合預(yù)期的‘mock’的 Foo 類來代替實(shí)際的 Foo 類,從而將測試關(guān)注點(diǎn)聚集到 hello() 方法,也避免了 Foo 類 sayHello() 方法的具體實(shí)現(xiàn)對測試可能帶來的影響。

    以上就是 DIP 最常見也是最令人熟悉的一層涵義,從 IOC、spring framework 開始接受 DIP,自然而然會(huì)把 DIP 等同于 DI 模式,但其實(shí)它還有另一層涵義。在《Agile Software Developement:principles,Patterns,and Practices》的第11章 依賴倒置原則,給出了 DIP 的另一種解讀:DIP —— 依賴倒置原則,這里的 DIP 就不再是 Dependency Injection Pattern(依賴注入模式),而是 Dependency Inversion Principle(依賴倒置原則,以下簡稱 DI 原則)。DI 原則主要包括下面兩條啟發(fā)式規(guī)則:
        A. 高層模塊不應(yīng)依賴于低層模塊。二者都應(yīng)依賴于抽象
        B. 抽象不應(yīng)依賴于細(xì)節(jié)。細(xì)節(jié)應(yīng)依賴于抽象

    人們通常會(huì)用好萊塢法則來詮釋啟發(fā)規(guī)則 A:“Don't call us, we'll call you.”其中的 we/us,就是指高層模塊,you 則是低層模塊,高層模塊來決定低層的模塊,就像好萊塢制片人最終掌握著演員上鏡與否的生殺大權(quán)。

    我們來看看軟件系統(tǒng)中常見的類關(guān)系,高層的 Policy Layer 使用了低層的 Mechanism Layer,而低層的 Mechanism Layer 又使用了更低層的 Utility Layer(見圖1)


    圖1中的依賴關(guān)系是傳遞的:高層的 Policy Layer 對于其下一直到 Utility Layer 的改動(dòng)都是敏感的,一旦我們修改了低層模塊的實(shí)現(xiàn),高層模塊不得不也修改相應(yīng)的實(shí)現(xiàn)來適應(yīng)低層模塊的修改。這種依賴關(guān)系,與軟件復(fù)用的目標(biāo)是相悖的,而且也不符合實(shí)際的業(yè)務(wù)變化。我們通常會(huì)需要切換低層的實(shí)現(xiàn)方式或者版本,而很少會(huì)去修改高層的業(yè)務(wù)。比如有一個(gè)證書申請發(fā)放系統(tǒng),需要使用異步的消息機(jī)制來處理用戶請求。客戶可能會(huì)要求把底層的 MessageQueue 從 IIS 切換成 ActiveQ,但高層的證書申請發(fā)放的業(yè)務(wù)流程是穩(wěn)定的,不會(huì)因低層基礎(chǔ)服務(wù)的改變而作出修改。

    所以,為了應(yīng)付現(xiàn)實(shí)中的變化,也為了向高層屏蔽這些變化,我們通常會(huì)給低層模塊抽象出接口,作為高層和低層之間的契約。這樣,高層模塊就應(yīng)該只與低層模塊的接口打交道,不再關(guān)心低層模塊的實(shí)現(xiàn)細(xì)節(jié)。而低層模塊的實(shí)現(xiàn),我們可以通過配置文件由框架來切換,不影響到高層的行為。說到這里,大家應(yīng)該可以看到 Spring 的影子了。此時(shí)類關(guān)系圖就變成了下圖(圖2)


    嗯,現(xiàn)在的類關(guān)系已經(jīng)遵循接口依賴了,甚至加上了框架的 DI 模式,系統(tǒng)也具有了一定的靈活性和易改變性,看起來很像大多數(shù)的系統(tǒng)了。但是,這是不是就是符合 DI 原則呢?我們可以看到,這里的接口通常是由低層模塊來定義和派生,也就是低層模塊抽象出來提供給外界調(diào)用的服務(wù)接口。高層模塊其實(shí)還是依賴于低層模塊,只是這次高層模塊產(chǎn)生依賴的是低層模塊里面聲明的接口。想起曾經(jīng)在 javaeye 上看到一篇帖子,作者抱怨為什么 java 不提供 extracts 關(guān)鍵字,這樣就可以由框架或者容器在具體類上抽出接口定義(與 implements 對應(yīng)),省得手工創(chuàng)建這些接口?;蛟S這是目前依賴注入框架帶來的誤區(qū)吧:使用人員不理解 DIP 的本意,單純是為框架的約束而創(chuàng)建。此時(shí)聲明的類關(guān)系顯然還是沒有完全體現(xiàn) DI 原則的精髓。

    在書中 Robert 進(jìn)一步對 DI 原則給出了解釋:“請注意這里的倒置不僅僅是依賴關(guān)系的倒置,也是接口所有權(quán)的倒置。我們通常會(huì)認(rèn)為工具庫應(yīng)該擁有它們自己的接口,但是應(yīng)用 DIP (DI 原則)時(shí),我們發(fā)現(xiàn)往往是消費(fèi)者擁有抽象接口,而它們的服務(wù)者則從這些接口派生。”基于這種思路,前面例子更符合面向?qū)ο笏枷氲膶哟侮P(guān)系圖如下(圖3)


    圖3和圖2的區(qū)別主要在接口的所有權(quán):高層模塊應(yīng)該擁有接口所有權(quán),低層模塊派生自高層模塊里定義的接口。這就意味著:
        1. 高層模塊引用的接口定義應(yīng)該和高層模塊的其他類放在一起
        2. 高層模塊的復(fù)用是把高層擁有的所有類和接口定義作為一個(gè)整體來復(fù)用的
        3. 接口定義的改變只有根據(jù)高層模塊的需要才進(jìn)行的,而不是低層模塊

    OK,直到現(xiàn)在,我們才能說 DI 原則原來是這個(gè)意思,否則,體會(huì)不到就有可能誤人誤己。Robert 在本章的結(jié)論中說“事實(shí)上,這種依賴關(guān)系的倒置正是好的面向?qū)ο笤O(shè)計(jì)的標(biāo)志所在,使用何種語言來編寫程序是無關(guān)緊要的。如果程序的依賴關(guān)系是倒置的,它就是面向?qū)ο蟮脑O(shè)計(jì)。如果程序的依賴關(guān)系不是倒置的,它就是過程化的設(shè)計(jì)。”信哉此言!

    那么,要按照 OOA & OOD 的設(shè)計(jì)思路來進(jìn)行系統(tǒng)設(shè)計(jì),DI 原則對我們有什么幫助呢?其實(shí),DI 原則不僅僅是抽象的原則,而且是可以啟發(fā)推導(dǎo)出出種種具體的實(shí)踐。我們來看看對 OOA & OOD 的幫助。因?yàn)橐蕾囮P(guān)系是倒置的,就可以通過對高層策略的抽象和定義驅(qū)動(dòng)出低層服務(wù)者的接口。以此類推,直到把最底層的模塊設(shè)計(jì)出為止。那什么是高層策略呢?怎么找出潛在的抽象?書中同樣給出了答案:“它是應(yīng)用背后的抽象,是那些不隨具體細(xì)節(jié)的改變而改變的真理。它是系統(tǒng)內(nèi)部的系統(tǒng)——它是隱喻(metaphore)”更有詳細(xì)的具體實(shí)踐,敬請關(guān)注本博其他文章。


    References:
    Inversion of Control Containers and the Dependency Injection pattern,http://www.martinfowler.com/articles/injection.html,Martin Fowler
    控制反轉(zhuǎn)與依賴注入模式,http://gigix.blogdriver.com/diary/gigix/inc/DependencyInjection.pdf,熊節(jié) 譯
    《Agile Software Developement:principles,Patterns,and Practices》,Robert Fowler 著,鄧輝 譯,孟巖 審


    評論

    # re: DIP 沉思錄  回復(fù)  更多評論   

    2008-11-02 20:02 by paul xu
    我覺得可以這樣理解:高層稱為使用者,低層稱為提供者,oo的設(shè)計(jì)應(yīng)該是首先有使用者,(因?yàn)橹挥惺褂谜卟胖雷约盒枰裁矗?,根?jù)使用者的需要,再去構(gòu)建低層提供者

    # re: DIP 沉思錄  回復(fù)  更多評論   

    2008-11-02 20:30 by mingj
    @paul xu
    對, 消費(fèi)者以及服務(wù)者提供了類似的定義
    其實(shí)這也是提供了一種構(gòu)建系統(tǒng)的方法

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


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 亚洲欧美精品午睡沙发| 无码的免费不卡毛片视频| 免费无码又爽又高潮视频| 又硬又粗又长又爽免费看 | 女性自慰aⅴ片高清免费| 伊人久久国产免费观看视频| 日韩精品一区二区亚洲AV观看 | 十八禁的黄污污免费网站| 亚洲AV无码国产精品麻豆天美| 免费观看无遮挡www的小视频| 午夜亚洲国产理论片二级港台二级| 久久亚洲中文字幕精品一区| 日本免费人成在线网站| 午夜成人无码福利免费视频| 亚洲网站在线播放| 亚洲一级特黄无码片| 无人在线观看免费高清视频 | 亚洲一区在线免费观看| 国产亚洲女在线线精品| 久久精品视频亚洲| 日本黄色免费观看| 无码精品人妻一区二区三区免费看 | 亚洲精品无码mv在线观看网站| 精品国产无限资源免费观看| xxxx日本在线播放免费不卡| 亚洲videos| 国产精品亚洲а∨无码播放| 国产精品免费视频播放器| 在线免费观看你懂的| 一级做a爰片久久免费| 亚洲区日韩精品中文字幕| 亚洲国产精品一区二区第一页| 国产免费卡一卡三卡乱码| 中文字幕免费高清视频| 一级毛片免费播放视频| 亚洲最大无码中文字幕| 亚洲美女aⅴ久久久91| 亚洲精品乱码久久久久久蜜桃不卡 | 亚洲三级视频在线| 久久精品亚洲日本佐佐木明希| 亚洲国产午夜中文字幕精品黄网站|