一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

從架構(gòu)到設(shè)計(jì)

 ShaneWu 2009-11-12
第四回:依賴的哲學(xué)(上)

本文將介紹以下內(nèi)容:

  • 關(guān)于依賴和耦合
  • 面向抽象編程
  • 依賴倒置原則
  • 控制反轉(zhuǎn)
  • 依賴注入
  • 工廠模式
  • Unity框架應(yīng)用
 
說在,開篇之前
在 老子的“小國寡民”論中,提出了一種理想的社會(huì)狀態(tài):民至老死,不相往來。這是他老人家的一種社會(huì)理想,老死不相往來的人群呈現(xiàn)了一片和諧景象。因?yàn)椴话l(fā) 生瓜葛,也就無所謂關(guān)聯(lián),進(jìn)而無法倒置沖突。這是先祖哲學(xué)中的至純哲理,但理想的大同總是和現(xiàn)實(shí)的生態(tài)有著或多或少的差距,人類社會(huì)無法避免聯(lián)系的發(fā)生, 所以小國寡民的理想成為一種美麗的夢想,不可實(shí)現(xiàn)。同樣的道理,映射到軟件“社會(huì)”中,也就是軟件系統(tǒng)結(jié)構(gòu)中,也預(yù)示著不同的層次、模塊、類型之間也必然 存在著或多或少的聯(lián)系,這種聯(lián)系不可避免但可管理。正如人類社會(huì)雖然無法實(shí)現(xiàn)小國寡民,但是理想的狀態(tài)下我們推崇和諧社會(huì),把人群的聯(lián)系由復(fù)雜變?yōu)楹唵危? 由曲折變?yōu)榻y(tǒng)一,同樣可以使得這種關(guān)聯(lián)很和諧。所以,軟件系統(tǒng)的使命也應(yīng)該朝著和諧社會(huì)的目標(biāo)前進(jìn),對于不同的關(guān)系處理,使用一套行之有效的哲學(xué),把復(fù)雜 問題簡單化,把僵化問題柔性化,這種哲學(xué)或者說方法,在我看來就是:依賴的哲學(xué),也就是本文所要闡釋的中心思想。

*Hot:《你必須知道的.NET》

 

1 引言

因?yàn)樵诠緝?nèi)部進(jìn)行設(shè)計(jì)原則和設(shè)計(jì)模式的培訓(xùn),我的第一個(gè)任務(wù)就是和大家就依賴倒置原則進(jìn)行溝通。作為5大設(shè)計(jì)原則之一的DIP原則,單純的由概念而實(shí)例在我認(rèn)為并不能完全闡釋清楚:

  • 什么是依賴倒置?
  • 為什么依賴倒置?
  • 如何依賴倒置?

這幾個(gè)關(guān)鍵的問題,所以我決定不單純的通過DIP而DIP,而是從依賴這個(gè)最原始的概念講起,來了解在面向?qū)ο筌浖O(shè)計(jì)體系中,關(guān)于“關(guān)系的處 理”,也就是“依賴的哲學(xué)”。對,依賴就是關(guān)系,處理依賴也就意味著處理關(guān)系。因?yàn)?,我們?nèi)祟愂亲钌朴诟汴P(guān)系的動(dòng)物,所以原本可以簡單的理論,在人類的意 識(shí)哲學(xué)中變得復(fù)雜而多變,以至于我們本應(yīng)簡單的道理變得如此復(fù)雜,這就是依賴。那么,從依賴講起來了解依賴倒置原則,我覺得首先應(yīng)該回到以下的問題:

  • 控制反轉(zhuǎn)、依賴倒置、依賴注入這些概念,你認(rèn)識(shí)但是否熟悉?
  • Unity、ObjectBuilder、Castle這些容器,你相識(shí)但是否相知?
  • 面向接口、面向抽象、開放封閉,這些思想,你了解但是否了然?

帶著對這些問題的思考和思索,Anytao帶領(lǐng)大家就依賴這個(gè)話題開始一次循序漸進(jìn)的面向?qū)ο笾?,以解答這些從一開始就有足夠吸引力的問題,從原理到實(shí)例,從關(guān)系到異同,我期待這篇文章能帶來一些認(rèn)知的變革。  

2 什么是依賴,什么是抽象

2.1 關(guān)于依賴和耦合:由小國寡民到和諧社會(huì)

在老子的“小國寡民”論中,提出了一種理想的社會(huì)狀態(tài):民至老死,不相往來。這是他老人家的一種社會(huì)理想,老死不相往來的人群呈現(xiàn)了一片和諧景象。 因?yàn)椴话l(fā)生瓜葛,也就無所謂關(guān)聯(lián),進(jìn)而無法倒置沖突。這是先祖哲學(xué)中的至純哲理,但理想的大同總是和現(xiàn)實(shí)的生態(tài)有著或多或少的差距,人類社會(huì)無法避免聯(lián)系 的發(fā)生,所以小國寡民的理想成為一種美麗的夢想,不可實(shí)現(xiàn)。同樣的道理,映射到軟件“社會(huì)”中,也就是軟件系統(tǒng)結(jié)構(gòu)中,也預(yù)示著不同的層次、模塊、類型之 間也必然存在著或多或少的聯(lián)系,這種聯(lián)系不可避免但可管理。正如人類社會(huì)雖然無法實(shí)現(xiàn)小國寡民,但是理想的狀態(tài)下我們推崇和諧社會(huì),把人群的聯(lián)系由復(fù)雜變 為簡單,由曲折變?yōu)榻y(tǒng)一,同樣可以使得這種關(guān)聯(lián)很和諧。所以,軟件系統(tǒng)的使命也應(yīng)該朝著和諧社會(huì)的目標(biāo)前進(jìn),對于不同的關(guān)系處理,使用一套行之有效的哲 學(xué),把復(fù)雜問題簡單化,把僵化問題柔性化,這種哲學(xué)或者說方法,在我看來就是:依賴的哲學(xué),也就是本文所要闡釋的中心思想。
因?yàn)椋?#8220;耦合是不可避免的”,所以我們首先就從認(rèn)識(shí)依賴和耦合的概念開始,來一步步闡釋我們的依賴哲學(xué)思想:

  • 什么是依賴和耦合

依賴,就是關(guān)系,代表了軟件實(shí)體之間的聯(lián)系。軟件的實(shí)體可能是模塊,可能是層次,也可能是具體的類型,不同的實(shí)體直接發(fā)生依賴,也就意味著發(fā)生了耦 合。所以,依賴和耦合在我看來是對一個(gè)問題的兩種表達(dá),依賴闡釋了耦合本質(zhì),而耦合量化了依賴程度。因此,我們對于關(guān)系的描述方式,就可以從兩個(gè)方面的觀 點(diǎn)來分析:


從依賴的角度而言,可以分類為:

    • 無依賴,代表沒有發(fā)生任何聯(lián)系,所以二者相互獨(dú)立,互不影響,沒有耦合關(guān)系。
    • 單向依賴,關(guān)系雙方的依賴是單向的,代表了影響的方向也是單向的,其中一個(gè)實(shí)體發(fā)生改變,會(huì)對另外的實(shí)體產(chǎn)生影響,反之則不然,耦合度不高。
    • 雙向依賴,關(guān)系雙方的依賴是相互的,影響也是相互的,耦合度較高。


從耦合的角度而言,可以分類為(此處回歸到具體的代碼級(jí)耦合概念,以方便概念的闡釋):

    • 零耦合,表示兩個(gè)類沒有依賴。
    • 具體耦合,如果一個(gè)類持有另一個(gè)具體類的引用,那么這兩個(gè)類就發(fā)生了具體耦合關(guān)系。所以,具體耦合發(fā)生在具體類之間的依賴,因此具體類的變更將引起對其關(guān)聯(lián)類的影響。
    • 抽象耦合,發(fā)生在具體類和抽象類的依賴,其最大的作用就是通過對抽象的依賴,應(yīng)用面向?qū)ο蟮亩鄳B(tài)機(jī)制,實(shí)現(xiàn)了靈活的擴(kuò)展性和穩(wěn)定性。


不同的耦合,代表了依賴程度的差別,我們以“粒度”為概念來分析其耦合的程度。引用中間層來分離耦合,可以使設(shè)計(jì)更加的優(yōu)雅,架構(gòu)更加的柔 性,但直接的依賴也存在其市場,過度的設(shè)計(jì)也并非可取之道。因?yàn)樾逝c性能同樣是設(shè)計(jì)需要考量的因素,過多的不必要分離會(huì)增加調(diào)用的次數(shù),造成效率浪費(fèi)。 在下文分析依賴倒置原則的弊端之一正是對此問題的進(jìn)一步闡述。

  • 耦合是如何產(chǎn)生的?

那么,軟件實(shí)體之間的耦合是如何產(chǎn)生呢?回歸我們每天揮灑的代碼片段,其實(shí)我們在重復(fù)的創(chuàng)造著耦合,并且得益于對這種耦合帶來的數(shù)據(jù)通信。如果我們將歷史的目光回歸到軟件設(shè)計(jì)之初,人類以簡單的機(jī)器語言來實(shí)現(xiàn)最簡單的邏輯,給一個(gè)輸入,實(shí)現(xiàn)一個(gè)輸出,可以表達(dá)為:

隨著軟件世界的革命,業(yè)務(wù)邏輯的復(fù)雜,以上的簡單化處理已經(jīng)不足以實(shí)現(xiàn)更復(fù)雜的軟件產(chǎn)品,在系統(tǒng)內(nèi)部的復(fù)雜度成為一個(gè)超越人腦可識(shí)別的程度時(shí),例如:

 

因此,人類開始發(fā)揮重組和簡單化處理的優(yōu)勢,我們不得不在軟件設(shè)計(jì)上做出平衡。平衡的結(jié)果就是通過對復(fù)雜的系統(tǒng)模塊化,把復(fù)雜問題簡單處理,從而達(dá) 到能夠被人腦識(shí)別的目的?;谶@種指導(dǎo)原則,隨著復(fù)雜度的增加模塊的劃分更加朝著精細(xì)化發(fā)展,尤其是面向?qū)ο蟪绦蛟O(shè)計(jì)理論的出現(xiàn),使得對復(fù)雜的處理實(shí)現(xiàn)了 更科學(xué)的理論基礎(chǔ)。然而,復(fù)雜的問題可以通過劃分實(shí)現(xiàn)簡單的功能模塊或者技術(shù)單元,但由此應(yīng)運(yùn)而生的子單元會(huì)越來越多,而且越來越多的子單元必須發(fā)生數(shù)據(jù) 的通信才能完成統(tǒng)一的業(yè)務(wù)處理,所以產(chǎn)生的數(shù)據(jù)通信管理也越來越多。對于子單元的管理,也就是我們本文關(guān)注的核心概念-依賴,成為新的軟件設(shè)計(jì)問題,那么 總結(jié)前人的經(jīng)驗(yàn),提煉今人的智慧,我們對耦合的產(chǎn)生做以如下歸納:

    • 繼承
    • 聚合
    • 接口
    • 方法調(diào)用和引用
    • 服務(wù)調(diào)用

了解了耦合發(fā)生的一般方式,我們就可以進(jìn)入了核心思想的討論,那就是在認(rèn)識(shí)依賴和了解依賴的基礎(chǔ)上,我們最終追求的目標(biāo)。

  • 設(shè)計(jì)的目標(biāo):高內(nèi)聚(High cohesion)、低耦合(Low coupling)

討論了半天,終于是時(shí)候?qū)σ蕾嚭婉詈线M(jìn)行一點(diǎn)兒總結(jié)了,也是該進(jìn)行一點(diǎn)目標(biāo)訴求了。在軟件設(shè)計(jì)領(lǐng)域,有那么幾個(gè)至高原則值得我們深刻心中,它們是:

    • 面向抽象編程
    • 低耦合,高內(nèi)聚
    • 封裝變化
    • 實(shí)現(xiàn)重用:代碼重用、算法重用

對了,就是這些平凡的字眼,匯集了面向?qū)ο笏枷氲暮诵膬?nèi)容,也是本文力求闡釋的禪意心經(jīng)。關(guān)于面向抽象編程和封裝變化,我們會(huì)在后面詳細(xì)闡釋,在此我們需要將注意力關(guān)注于“低耦合,高內(nèi)聚”這一目標(biāo)。


低耦合,代表了實(shí)現(xiàn)最簡單的依賴關(guān)系,盡可能的減少類與類、模塊與模塊、層次與層次、系統(tǒng)與系統(tǒng)之間的 聯(lián)系。低耦合,體現(xiàn)了人類追求簡單操作的理想狀態(tài),按照軟件開發(fā)的基本實(shí)現(xiàn)技巧來追求軟件實(shí)體之間的關(guān)系簡單化,正是大部分設(shè)計(jì)模式力圖追求的目標(biāo);低耦 合,降低了一個(gè)類或一個(gè)模塊發(fā)生修改對其他類或模塊造成的影響,將影響范圍簡單化。在我們闡釋的依賴關(guān)系方式中,實(shí)現(xiàn)單向的依賴,實(shí)現(xiàn)抽象的耦合,都是實(shí) 現(xiàn)低耦合的基礎(chǔ)條件。


高內(nèi)聚,一方面代表了職責(zé)的統(tǒng)一管理,一方面體現(xiàn)了關(guān)系的有效隔離。例如單一職責(zé)原則其實(shí)歸根結(jié)底是對 功能性的一種指導(dǎo)性體現(xiàn),將功能緊密聯(lián)系的職責(zé)封裝為一個(gè)類(或模塊),而判斷的準(zhǔn)則正是基于引起類變化的原因。所以,封裝離不開依賴,而抽象離不開變 化,二者的概念和本質(zhì)都是相對而言的。因此,高內(nèi)聚的目標(biāo)體現(xiàn)了以隔離為目標(biāo)進(jìn)行統(tǒng)一管理的思想。
那么,為了達(dá)到低耦合、高內(nèi)聚的目標(biāo),通常意義上的設(shè)計(jì)原則和設(shè)計(jì)模式其實(shí)都是朝著這個(gè)方向?qū)崿F(xiàn)的,因此我們僅僅小結(jié)并非普遍意義的規(guī)則:

    • 盡可能實(shí)現(xiàn)單項(xiàng)依賴
    • 不需要進(jìn)行數(shù)據(jù)交換的雙方,不要實(shí)現(xiàn)多此一舉的關(guān)聯(lián),人們將此形象稱為,不要向陌生人說話(Don't talk to strangers)
    • 保持內(nèi)部的封裝性,關(guān)聯(lián)的雙方不要深入實(shí)現(xiàn)細(xì)節(jié)進(jìn)行通信,這是保證高內(nèi)聚的必須條件。

2.2 關(guān)于抽象和具體

什么是抽象呢?我們首先不必澄清什么是抽象,而從什么算抽象說起,穩(wěn)定的、高層的則代表了抽象。就像一個(gè)公司,最好保證了高層的穩(wěn)定,才能保證全局 的發(fā)展。在進(jìn)行系統(tǒng)設(shè)計(jì)時(shí),穩(wěn)定的抽象接口和高層邏輯,也代表了整個(gè)系統(tǒng)的穩(wěn)定與柔性。兵熊熊一窩,將良良一窩,系統(tǒng)的邏輯也正如著代表打仗,良好的設(shè)計(jì) 都是自上而下的。而對具體的編程實(shí)踐而言,接口和抽象類則代表了語言層次的抽象。
追溯概念的分析,我們一一過招,首先來看依賴于具體:

 

因此,為了分離這種緊耦合,最好的辦法就是隔離,引入中間層來分離變化,同時(shí)確保中間層本身的穩(wěn)定性,因此抽象的中間層是最佳的選擇。

 

例如:

    public interface IUserService
    {
    }
 
    public class UserService : IUserService
    {
    }

下面依賴于具體:

    public class UserManager
    {
        private UserService service = null;
    }

下面依賴于抽象:

    public class UserManager
    {
        private IUserService service = null;
    }

二者的區(qū)別僅在于引入了接口IUserService,從而使得UserManager對于UserService的依賴由強(qiáng)減弱。這種方式也在我們的Ezsocio項(xiàng)目中進(jìn)行service層的設(shè)計(jì)方式。然而對于依賴的方式并非僅此一種,設(shè)計(jì)模式中的智慧正是通過各章編程技巧進(jìn)行依賴關(guān)系的設(shè)計(jì),值得我們關(guān)注和學(xué)習(xí),本文也在下文進(jìn)行相關(guān)設(shè)計(jì)模式的討論。
對WCF熟悉的讀者一定不難看出這種實(shí)現(xiàn)方式如此類似于WCF的推薦模式,這是契約編程的基本思想。關(guān)于WCF及SOA的相關(guān)內(nèi)容,本文將在后文進(jìn)行相關(guān)的討論。
總結(jié)一番,什么是抽象,什么是具體?在我看來,抽象就是系統(tǒng)中對變化封裝的戰(zhàn)略邏輯,體現(xiàn)了系統(tǒng)的必然性和穩(wěn)定性,能夠被具體層次復(fù)用和覆寫;而具體則包含了與具體實(shí)現(xiàn)相關(guān)的邏輯,體現(xiàn)了系統(tǒng)的動(dòng)態(tài)性和變動(dòng)性。因此,抽象是穩(wěn)定的,而具體是變動(dòng)的。
Bob 大叔在《敏捷》一書直言,程序中所有的依賴關(guān)系都應(yīng)終止于抽象類或者接口,就是對面向抽象編程一針見血的回應(yīng),其原因歸根結(jié)底源自于我們對抽象和具體的認(rèn) 知和分解:關(guān)聯(lián)應(yīng)該終止于抽象,而不是具體,保證了系統(tǒng)依賴關(guān)系的穩(wěn)定。具體類發(fā)生的修改,不會(huì)影響其他模塊或者關(guān)系。那么如何做到這種理想的依賴于抽象 的設(shè)計(jì)呢?

  • 層次清晰化

將復(fù)雜的問題簡單化,是人類思維的一般智慧,也自然而然是實(shí)現(xiàn)軟件設(shè)計(jì)的基本思路。而將復(fù)雜的業(yè)務(wù)需求通過建模過程的抽象化提煉,去粗取精,去偽存真,凡此種種。而抽象的過程,其目標(biāo)之一就是形成對于復(fù)雜問題簡單化的處理過程,只有形成層次簡單的邏輯才能將復(fù)雜需求中的關(guān)系梳理清晰,而依賴的本質(zhì)正如上文所言,不就是處理關(guān)系嗎?
所以,清晰的層次劃分,進(jìn)而形成的模塊化,是實(shí)現(xiàn)系統(tǒng)抽象的必經(jīng)之路。

  • 分散集中化

由需求而設(shè)計(jì)的過程,就是一個(gè)分散集中化的過程,把需求相關(guān)的業(yè)務(wù)通過開發(fā)流程的需求分析過程進(jìn)行整理,逐步形成需求規(guī)格說明、概要設(shè)計(jì)和詳細(xì)設(shè)計(jì) 等基本流程。分散集中化,是一個(gè)梳理需求到形成設(shè)計(jì)的過程,因此對于把握系統(tǒng)中的抽象和具體而言,是一個(gè)重要的分析過程和手段。現(xiàn)代軟件工程已經(jīng)對此形成 了科學(xué)的標(biāo)準(zhǔn)化流程處理邏輯,例如可以借助UML更加清晰的設(shè)計(jì)流程、分析設(shè)計(jì)要素,進(jìn)行標(biāo)準(zhǔn)化溝通和交流。

  • 具體抽象化

將具體問題抽象化,是本節(jié)關(guān)注的要點(diǎn),而處理的方法是什么呢?答案就在設(shè)計(jì)模式,設(shè)計(jì)模式是前輩智慧的總結(jié)和實(shí)踐,所以熟悉和學(xué)習(xí)設(shè)計(jì)模式,是學(xué)習(xí) 和實(shí)踐設(shè)計(jì)問題的必經(jīng)之路。然而,沒有哪個(gè)問題是由設(shè)計(jì)模式全權(quán)解決,也沒有那個(gè)模式能夠適應(yīng)所有的問題,因此我們要努力的是盡量積累更多的模式來應(yīng)對多 變的需求。作為軟件設(shè)計(jì)話題中最重量級(jí)的話題,我也會(huì)在以后的歲月中對設(shè)計(jì)模式問題進(jìn)行一些探討。

  • 封裝變化點(diǎn)

總的來說,抽象和變化就像一對孿生兄弟,將具體的變化點(diǎn)隔離出來以抽象的方式進(jìn)行封裝,在變化的地方尋找抽象是面對抽象最理想的方式。所以,如何去 尋找變化是設(shè)計(jì)要解決的首頁問題,例如工廠模式的目標(biāo)是封裝對象創(chuàng)建的變化,橋接模式封裝的是對象間的依賴關(guān)系變化等等。23個(gè)經(jīng)典的設(shè)計(jì)模式,從某種角 度來看,正是對不同變化點(diǎn)的封裝角度提出的不同解決方案。
這一設(shè)計(jì)原則中我們還將之稱為SoC(Separation of Concerns)原則,定義了對于實(shí)現(xiàn)理想的高耦合、低內(nèi)聚目標(biāo)的統(tǒng)一規(guī)則。 

2.3 設(shè)計(jì)的哲學(xué)

之所以花如此篇幅來講述一個(gè)看似簡單的問題,其實(shí)最終理想是回歸到軟件設(shè)計(jì)目標(biāo)這個(gè)命題上。如果悉心鉆研就可發(fā)現(xiàn),設(shè)計(jì)的最后就是對關(guān)系的處理,正 如同生活的意義在于對社會(huì)的適應(yīng)一樣。因此,回歸到設(shè)計(jì)的目標(biāo)上我們就可知,完美的設(shè)計(jì)過程就是對關(guān)系的處理過程,也就是對依賴的梳理過程,并最終形成一 種合理的耦合結(jié)果。
所以,面向?qū)ο蟛⒉簧衩?,我們以生活的現(xiàn)實(shí)眼光來看更是如此。把面向?qū)ο笊疃葷饪s起來,我覺得可以概括為:

  • 目標(biāo):重用、擴(kuò)展
  • 核心:低耦合、高內(nèi)聚
  • 手段:封裝變化
  • 思想:面向接口編程、面向抽象編程


其實(shí),就是這么簡單。在這種意義上來說,面向?qū)ο笏枷胧乾F(xiàn)代軟件架構(gòu)設(shè)計(jì)的基礎(chǔ)。下面我們以三層架構(gòu)的設(shè)計(jì)為例,來進(jìn)一步感受這種依賴哲學(xué) 的具體應(yīng)用。關(guān)于依賴的抽象和對變化隔離的基本思路,其實(shí)也是實(shí)現(xiàn)我們典型三層架構(gòu)(或者)多層架構(gòu)的重要基礎(chǔ)。只要使各個(gè)層次之間依賴于較穩(wěn)定的接口, 才能使得各個(gè)層次之間的變化被隔離在本層之內(nèi),不會(huì)造成對其他層次的影響,這完全符合開放封閉原則追求的優(yōu)良設(shè)計(jì)理念。將這種思路表達(dá)為設(shè)計(jì),可以表示 為:

在此,IDataProvider作為隔離業(yè)務(wù)層和數(shù)據(jù)層的抽象,IService作為隔離業(yè)務(wù)層和表現(xiàn)層的抽象,保證了各個(gè)層次的相對穩(wěn)定和封 裝。而體現(xiàn)在此的設(shè)計(jì)邏輯,就正是我們對于抽象和耦合基本目標(biāo)概念的體現(xiàn),例如作為重用的單元,抽象隔離保證了對外發(fā)布接口的單一和穩(wěn)定,所以達(dá)到了最高 限度的重用;通過引入中間的穩(wěn)定的接口,達(dá)到了不同層次的有效隔離,層與層之間體現(xiàn)為輕度耦合,業(yè)務(wù)層只持有IDataProvider就可以獲取數(shù)據(jù)層 的所有服務(wù),而表現(xiàn)層也同樣如此;最后,這種方式顯然也直接實(shí)踐了面向接口編程,面向抽象編程的經(jīng)典理念。
同樣的道理,對于架構(gòu)設(shè)計(jì)的很多概 念,放大可以擴(kuò)展為面向服務(wù)設(shè)計(jì)所借鑒,放小這正是我們反復(fù)降調(diào)的依賴倒置原則在類設(shè)計(jì)中的基本思想。因此,牢記對我影響至深的一位大牛的說法:軟件設(shè)計(jì) 的任何問題,都可以通過引入中間邏輯了解決。而這個(gè)中間邏輯,很多時(shí)候被封裝為抽象,是最為合理和智慧的解決方案。
讓我們再次高頌《老子》的小國寡民論,來回味關(guān)于依賴哲學(xué)中,我們?nèi)绾螌?shí)現(xiàn)更好的和諧統(tǒng)一,如何遵守科學(xué)的軟件管理思想:"鄰國相望,雞犬之聲相聞,民至老死,不相往來。"

3 認(rèn)識(shí)依賴倒置原則(DIP)

3.1 什么是依賴倒置?

Bob大叔在《Agile Principles, Patterns, and Practices》一書中對依賴倒置原則進(jìn)行了精辟的總結(jié)為:

  • 高層模塊不應(yīng)該依賴于低層模塊,二者都應(yīng)該依賴于抽象。
  • 抽象不應(yīng)該依賴于具體,細(xì)節(jié)應(yīng)該依賴于抽象。

我規(guī)規(guī)矩矩一字不差的把上述真言放在心里,卻發(fā)現(xiàn)大師的牛論實(shí)在有點(diǎn)故作玄虛,就像欣賞Bob在論述DIP時(shí)的插畫一樣費(fèi)解不討好:

 

其實(shí)著名的好萊塢原則更形象的闡述了這一思想:你不要調(diào)我,我來調(diào)你。不管是通俗的還是高尚的,卻都不約而同的揭示了依賴倒置原則的最核心思想就是:

依賴于抽象,對接口編程,對抽象編程!

相較而言,從實(shí)際的生活中來看依賴倒置,就像下面這個(gè)示例揭示的一樣。

3.2 從實(shí)例開始

綜合對依賴倒置的認(rèn)識(shí),結(jié)合到具體的程序?qū)崿F(xiàn)而言,依賴倒置預(yù)示著程序中的依賴關(guān)系不應(yīng)是具體的類型,而是歸咎于抽象類和接口。下面我們通過一個(gè)簡 單的實(shí)例來分析符合依賴倒置和違反依賴倒置,對于系統(tǒng)設(shè)計(jì)的影響和區(qū)別。我們的需求是為某個(gè)遙控器生產(chǎn)商,實(shí)現(xiàn)一個(gè)萬能遙控器,該遙控器可以對當(dāng)前市場上 的很多電子設(shè)備進(jìn)行“打開”和“關(guān)閉”的操作,例如你可以使用Anytao牌遙控器打開海爾電視、創(chuàng)維電視等等,當(dāng)然更理想的狀態(tài)是可以打開電冰箱、電燈 還有門窗等等,總之凡是可以互聯(lián)的設(shè)備都是未來萬能遙控器的新需求。
那么該遙控器廠商在設(shè)計(jì)之初,該如何去考慮實(shí)現(xiàn)一個(gè)可以打開任何設(shè)備的遙控器呢?這一重責(zé)首先落在了一位年輕氣盛的小王設(shè)計(jì)師身上,因?yàn)檫b控器廠家當(dāng)前的直接客戶只有海爾電視一家,所以他輕松的實(shí)現(xiàn)了下面的設(shè)計(jì),并且興高采烈的進(jìn)行了大批量生產(chǎn):

隨后,廠商多了一個(gè)重量級(jí)客戶長虹,所以小王不得不對初試設(shè)計(jì)進(jìn)行了改造,勉強(qiáng)適應(yīng)了新的需求,如下:

雖然小王應(yīng)付了這次需求變動(dòng),但是原本的設(shè)計(jì)顯然已經(jīng)捉襟見肘。正當(dāng)小王絞盡腦汁進(jìn)行改造的同時(shí),新的需求接踵而來:新飛冰箱、飛利浦照明、盼盼防盜門,一個(gè)接一個(gè)。小王的最終設(shè)計(jì)變成了這般摸樣:

 

哎,真是太累了。每一次的需求變更都伴隨著小王對遙控器Remote的再次摧殘,Remote內(nèi)部不斷增加新的引用和操作處理,顯然一個(gè) if/else式的判斷布滿了整個(gè)Open和Close的操作中,這種設(shè)計(jì)顯然無法滿足OCP對擴(kuò)展開放、對修改封閉的要求。顯然,如果想讓賣出去的遙控 器也適應(yīng)新的需求,在小王當(dāng)前的設(shè)計(jì)實(shí)現(xiàn)方案中是根本無法實(shí)現(xiàn)的,遙控器廠商總不能召回已經(jīng)售出所有的遙控器,再拆開進(jìn)行重新改造吧。
一籌莫展的小王,終于在崩潰之際想起了經(jīng)驗(yàn)豐富的前設(shè)計(jì)師老王,并立即請教如何解決當(dāng)前問題的思路。而老王也毫不含糊,給出了一個(gè)初步的實(shí)現(xiàn):

在當(dāng)前的設(shè)計(jì)中,老王的思路是讓遙控器廠切斷和各個(gè)廠家的直接聯(lián)系,而是尋找所有電視廠商的領(lǐng)導(dǎo)(例如,電視機(jī)協(xié)會(huì)),請電視機(jī)協(xié)會(huì)制定所有電視機(jī) 廠商必須遵守的打開和關(guān)閉等操作的契約,遙控器廠和電視機(jī)協(xié)會(huì)建立直接的聯(lián)系而不是各個(gè)具體的電視廠商,于是便有了上述設(shè)計(jì)思路。而新的需求來臨時(shí),因?yàn)? 各個(gè)廠商必須遵守TurnOn和TurnOff的契約,所以輕松的萬能遙控器可以應(yīng)付所有的電視機(jī)品牌,實(shí)現(xiàn)的具體操作已經(jīng)由遙控器轉(zhuǎn)移到具體的廠商手上 (順便說說這也是所有權(quán)的倒置體現(xiàn)),輕松的小王終于大呼一口氣。并且再接再厲修改了更完善的版本:

現(xiàn)在,遙控器基本實(shí)現(xiàn)了萬能的要求,任何新的需求或者修改都可以輕松勝任。小王終于解決了原本設(shè)計(jì)的所有問題,帶著感激盛情邀請老王吃飯致謝。席間 就坐,小王請教老王二次設(shè)計(jì)的秘訣,老王神秘一笑沾酒在桌子上寫了幾個(gè)大字:依賴倒置。經(jīng)歷此次設(shè)計(jì)重構(gòu)洗禮的小王,也在實(shí)戰(zhàn)中體味了設(shè)計(jì)的精妙,看著依 賴倒置幾個(gè)字小王也會(huì)心的笑了。
萬能遙控器的故事,是一個(gè)系統(tǒng)實(shí)現(xiàn)中經(jīng)常的事兒。而這些設(shè)計(jì)在Ezsocio項(xiàng)目中有廣泛的應(yīng)用,例如對于DataProvider和Service的處理方式,正是一種典型的遵循DIP原則的設(shè)計(jì)思路。

3.3 為什么依賴倒置?

依賴倒置原則揭示了面向?qū)ο笏枷胫幸粋€(gè)最基本而最核心的話題,那就是:面向抽象編程。任何對依賴倒置原則的違反都不同程度的偏離了面向?qū)ο笤O(shè)計(jì)思想的軌道,所以如果你想自己的程序是否足夠的OO,透徹的了解依賴倒置是必不可少的。
所以,要問答為什么依賴倒置這個(gè)話題,我覺得可以從以下幾個(gè)方面來闡釋:

  • 依賴倒置是保證開放封閉的前提和基礎(chǔ)。
  • 依賴倒置是對抽象和依賴的基本原則和基本思想的哲學(xué)闡釋。
  • 依賴倒置是框架設(shè)計(jì)的核心思想。
  • 依賴倒置是控制反轉(zhuǎn)和依賴注入的思想基礎(chǔ)。


綜上而言,依賴倒置是對軟件實(shí)體關(guān)系處理的基本思想原則,也是其他設(shè)計(jì)原則與設(shè)計(jì)模式的基礎(chǔ)之一,因此遵守依賴倒置是實(shí)現(xiàn)OO的基本原則,是我們必須了解的基礎(chǔ)性原則。下面,我們對此進(jìn)行詳細(xì)的說明和舉例。

3.4 為什么是倒置?

魯迅先生有云:其實(shí)地上本沒有路,走的人多了也便成了路。對依賴倒置原則中的“倒置”二字而言,其實(shí)也類似于一條被很多人走過的路,因?yàn)榱?xí)慣性的稱呼走過的為“路”,所以只好把違反習(xí)慣的東西稱為“倒置的路”。這倒置的含義,正基于此。
對于從結(jié)構(gòu)化編程走過的人來說,基于軟件復(fù)用的考慮,側(cè)重于對具體模塊的復(fù)用,因?yàn)橐簿土?xí)慣了從高層模塊出發(fā)了構(gòu)建系統(tǒng)流程的思維模式,所以那時(shí)的高手一出手就實(shí)現(xiàn)了高層依賴于底層的典型套路,例如:

高層模塊通過自上而下的實(shí)現(xiàn)來完成系統(tǒng)功能的調(diào)用,將這種方式表達(dá)為代碼就是:

        // Release : code01, 2008/11/02                    
        // Author  : Anytao, http://www. 
        public static void Main()
        {
            try
            {
                //Do something here.
            }
            catch
            {
                Log(true, "XMLLog");
            }
        }
 
        public static void Log(bool isRead, string logType)
        {
            if (isRead)
                ReadLog(logType);
            else
                WriteLog(logType);
        }

然而,當(dāng)軟件設(shè)計(jì)的模式發(fā)展到面向?qū)ο箅A段時(shí),我們發(fā)現(xiàn)原來習(xí)慣的世界了已經(jīng)變了?;诟邔右蕾囉诘讓拥谋渍?,也越來越被可擴(kuò)展性的系統(tǒng)需求折磨的 面目全非,例如如果日志記錄的載體發(fā)生變化,當(dāng)前設(shè)計(jì)中需要同時(shí)自上而下的修改實(shí)現(xiàn)的邏輯,同時(shí)避免出現(xiàn)越來越多的if/else結(jié)構(gòu)。所以當(dāng)新的依賴關(guān) 系從傳統(tǒng)的方式被完全扭轉(zhuǎn)時(shí),“倒置”二字就此誕生了。我們修改Log實(shí)現(xiàn)的設(shè)計(jì)思路,將可能變化的邏輯封裝為抽象接口,使得高層依賴發(fā)生轉(zhuǎn)換:

 

程序?qū)崿F(xiàn)的邏輯早已被面向?qū)ο蟮脑O(shè)計(jì)思想所取代,我們新的實(shí)現(xiàn)變成了:

    // Release : code02, 2008/11/02                    
    // Author  : Anytao, http://www. 
    public class Client
    {
        public static void Main()
        {
            ILog myLogger = new XMLLog();
            try
            {
 
            }
            catch
            {
                myLogger.Write();
            }
        }
    }
 
    public interface ILog
    {
        void Read();
        void Write();
    }
 
    public class XMLLog : ILog
    {
        public void Read()
        {
        }
 
        public void Write()
        { 
        }
    }

所以,了解了歷史才能正視現(xiàn)實(shí),對于軟件設(shè)計(jì)同樣如此,只有認(rèn)清楚依賴倒置產(chǎn)生的歷史背景,我們才能更加熟練的駕馭倒置含義本身帶來的誤解,而將中心思想牢牢的把握在依賴倒置最核心的設(shè)計(jì)思想上,那還是萬變不離其宗的:依賴于抽象,這簡單的5個(gè)字上。
對于所屬權(quán)關(guān)系的依賴問題上,我們看到,只有倒置的才是面向?qū)ο蟮?,沒有倒置的還是面向結(jié)構(gòu)的。如果你的系統(tǒng)中存在著不合理的依賴關(guān)系,那么依賴倒置將是檢查系統(tǒng)設(shè)計(jì)最好的標(biāo)尺,這也是我們把握這一原則的實(shí)際意義之一。

3.5 如何依賴倒置?

如何依賴倒置的關(guān)鍵,還是體現(xiàn)在如何對抽象和具體的封裝和分離,實(shí)踐的基本思路就是封裝變化。這正如我們在單一職責(zé)原則中反復(fù)強(qiáng)調(diào),對一個(gè)類只有一 個(gè)引起它變化的原因。我們實(shí)踐依賴倒置,仍然可以從關(guān)注變化開始,詳細(xì)的分析和預(yù)測系統(tǒng)中的變化點(diǎn),然后針對每個(gè)可能的變化抽象出相對穩(wěn)定的約束,這是我 們實(shí)踐依賴倒置原則最基本的方法步驟。
就原理而言,依賴倒置要求我們的設(shè)計(jì):

  • 少繼承,多聚合
  • 單向依賴(低耦合,高內(nèi)聚)
  • 封裝抽象
  • 對依賴關(guān)系都應(yīng)該終止于抽象類和接口


就實(shí)踐而言,經(jīng)典的軟件設(shè)計(jì)實(shí)踐為我們提出了很多值得借鑒的思路,例如每個(gè)設(shè)計(jì)模式就是對一種特定情況的實(shí)踐總結(jié),在此我們繼續(xù)列出一些經(jīng) 典的大師忠言,Bob大叔在《Agile Principles, Patterns, and Practices》一書對此進(jìn)行了3點(diǎn)總結(jié):

  • 任何變量都不應(yīng)該持有一個(gè)指向具體類的指針或者引用。
  • 任何類都不應(yīng)該從具體類派生。
  • 任何方法都不應(yīng)該覆寫它的任何基類中的已經(jīng)實(shí)現(xiàn)的方法。

實(shí)際上,在實(shí)際的設(shè)計(jì)過程中要完全遵守這幾點(diǎn)要求是有難度的,所以如何既能很好的遵守設(shè)計(jì)原則,又能很好的適應(yīng)代碼情況,是值得權(quán)衡的問題,需要我們不斷的積累和實(shí)踐。另外,還有幾個(gè)經(jīng)驗(yàn)只談:

  • 系統(tǒng)架構(gòu)應(yīng)該有清晰的層次定義,層次之間通過接口向外提供內(nèi)聚服務(wù),正如在三層示例中的舉例一樣。
  • 典型的以new進(jìn)行的對象創(chuàng)建操作,是對依賴倒置原則的典型違反,我們將在后文進(jìn)行詳細(xì)討論。


如何依賴倒置,我們闡釋了一點(diǎn)原則還有一點(diǎn)方法,算是對實(shí)現(xiàn)依賴倒置的一點(diǎn)小結(jié)。然而,在實(shí)際的開發(fā)過程中,并沒有一成不變的規(guī)則,當(dāng)前的 面向?qū)ο笳Z言本身就提供了對抽象和封裝的支持,為實(shí)現(xiàn)面向?qū)ο笤O(shè)計(jì)提供了基礎(chǔ)機(jī)制。回顧軟件開發(fā)的歷史,我們不難看出依賴和封裝哲學(xué)的發(fā)展軌跡,在結(jié)構(gòu)化 編程中函數(shù)是封裝的基本單元;隨著面向?qū)ο蟮陌l(fā)展C++/C#高級(jí)語言以類為基本單元,第一次將數(shù)據(jù)和行為有機(jī)的組合為一個(gè)邏輯單元,于是有了對于不同類 之間的關(guān)系處理哲學(xué);而SOA中封裝的單元上升為service,是一種更高意義的邏輯封裝,實(shí)現(xiàn)了更優(yōu)良的邏輯封裝和松散耦合關(guān)系。同樣的道理,也體現(xiàn) 在三層架構(gòu)的分割和通信中,體現(xiàn)在ORM對表現(xiàn)層和領(lǐng)域?qū)拥姆蛛x中。
因此,依賴倒置是一種高度的智慧和經(jīng)驗(yàn)總結(jié),如何實(shí)現(xiàn)依賴倒置也是一種積累和不斷的學(xué)習(xí)。 

3.6 也有弊端

然而,一味的遵守原則,就等于沒有原則。重要的是,我們需要把握其平衡,在進(jìn)行開發(fā)中適當(dāng)?shù)陌盐掌涑潭?。Bob在《敏捷》中也提到這個(gè)問題,他總結(jié)了依賴倒置的兩個(gè)弊端,同樣需要我們必要的關(guān)注:

  • 對抽象編程,需要增加必要的類和輔助代碼進(jìn)行支持,某種程度上增加了系統(tǒng)復(fù)雜度和維護(hù)成本;
  • 當(dāng)具體類不存在變化時(shí),遵守依賴倒置是多此一舉。所以,如果具體或細(xì)節(jié)沒有變化可能時(shí),我們沒有必要通過抽象轉(zhuǎn)嫁依賴是沒有必要的處理。

所以,學(xué)習(xí)模式或者原則必須把握靈活處理,不能一味強(qiáng)行。

 
下集預(yù)告 

在下篇中,我們將繼續(xù)對依賴相關(guān)的問題進(jìn)行討論,基本的內(nèi)容還包括:

  • 解構(gòu)控制反轉(zhuǎn)(IoC)和依賴注入(DI)
  • 基于契約編程:SOA架構(gòu)下的依賴
  • 相關(guān)的設(shè)計(jì)模式簡析
  • 對象創(chuàng)建的依賴

近期發(fā)布,敬請期待。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請遵守用戶 評(píng)論公約

    類似文章 更多

    真实偷拍一区二区免费视频| 人妻久久一区二区三区精品99| 欧美日韩人妻中文一区二区| 国产成人免费激情视频| 老司机精品在线你懂的| 欧美黑人黄色一区二区| 日韩av生活片一区二区三区| 国产福利一区二区久久| 欧美精品一区久久精品| 中国少妇精品偷拍视频| 福利视频一区二区在线| 国产亚洲欧美自拍中文自拍| 日韩精品成区中文字幕| 人妻人妻人人妻人人澡| 国产肥女老熟女激情视频一区| 老外那个很粗大做起来很爽| 男人和女人干逼的视频| 国产麻豆成人精品区在线观看| 99在线视频精品免费播放| 国产欧美性成人精品午夜| 久久亚洲精品中文字幕| 欧美大粗爽一区二区三区| 色综合久久超碰色婷婷| 99免费人成看国产片| 免费黄片视频美女一区| 亚洲欧美日韩国产自拍| 国产一区二区三区四区免费| 白丝美女被插入视频在线观看| 亚洲精品黄色片中文字幕| 国产av一区二区三区麻豆| 欧美黑人巨大一区二区三区| 精品香蕉国产一区二区三区| 日本在线高清精品人妻| 久久机热频这里只精品| 国产永久免费高清在线精品| 蜜桃av人妻精品一区二区三区| 91后入中出内射在线| 男人大臿蕉香蕉大视频| 美国女大兵激情豪放视频播放 | 国产精品一区二区日韩新区| 精品香蕉国产一区二区三区|