我是怎樣教媳婦面向?qū)ο缶幊痰?/h1>
我老婆 Farhana 想要繼續(xù)軟件開(kāi)發(fā)生涯(之前因?yàn)槲覀兊牡谝粋€(gè)孩子出生,她不得不放棄)。我已經(jīng)有了一些軟件設(shè)計(jì)和開(kāi)發(fā)的經(jīng)驗(yàn),所以這幾天我就在試著幫助她學(xué)習(xí)OOD。
由于我早年在軟件開(kāi)發(fā)的經(jīng)驗(yàn),我總是發(fā)現(xiàn)無(wú)論一個(gè)技術(shù)問(wèn)題看上去多么難搞,只要從現(xiàn)實(shí)生活的角度去解釋或用對(duì)話的方式去討論總能讓它變得更簡(jiǎn)單。關(guān)于OOD,我們已經(jīng)有了許多成果豐碩的討論,我覺(jué)得有人可能發(fā)現(xiàn)這是一個(gè)學(xué)習(xí)OOD有趣的方式,所以我想我應(yīng)該分享出來(lái)。
下面是我們的談話步驟:
話題:介紹面向?qū)ο笤O(shè)計(jì)
丈夫:親愛(ài)的,讓我們開(kāi)始學(xué)習(xí)面向?qū)ο笤O(shè)計(jì)。你了解面向?qū)ο笠?guī)范,對(duì)嗎?
妻子:你是指封裝,繼承和多態(tài)嗎?是的,我了解這些規(guī)范。
丈夫:行,我想你已經(jīng)知道怎么用類和對(duì)象了。今天我們來(lái)學(xué)習(xí)面向?qū)ο笤O(shè)計(jì)。
妻子:等等。了解面向?qū)ο笠?guī)范對(duì)面向?qū)ο缶幊虂?lái)說(shuō)難道不夠嗎?我的意思是,我能夠定義類,封裝屬性和方法。我能夠根據(jù)它們的關(guān)系定義類的繼承。那還有什么呢?
丈夫:很好的問(wèn)題。面向?qū)ο笠?guī)范和面向?qū)ο缶幊掏耆莾纱a事。讓我展示一個(gè)現(xiàn)實(shí)生活中的例子來(lái)幫助你理解它們。
我們從牙牙學(xué)語(yǔ)起,都是先從字母表學(xué)起的,對(duì)吧?
妻子: 嗯。
丈夫: 好,然后你就能認(rèn)單詞了,還能通過(guò)不同的字母拼寫出不同的單詞來(lái)。慢慢的,你能通過(guò)一些基本的語(yǔ)法把這些單詞串成一句話。為了使句子時(shí)態(tài)正確且沒(méi)有語(yǔ)病,你需要用一些介詞,連詞,等等。。看下面這句話
"I" (代詞) "want" (動(dòng)詞) "to" (介詞) "learn" (動(dòng)詞) "OOD" (名詞)
通過(guò)把幾個(gè)單詞擺放妥當(dāng)一句話就好了,然后用個(gè)關(guān)鍵詞來(lái)說(shuō)明一下這句話的重點(diǎn)。
妻子: 親愛(ài)的,你閑扯這些到底要說(shuō)明什么呢
丈夫: 我說(shuō)的這個(gè)例子跟面向?qū)ο笠?guī)范很類似,面向?qū)ο笠?guī)范為面向?qū)ο缶幊潭x了基本的規(guī)范,它是面向?qū)ο缶幊痰闹饕枷?。面向?qū)ο笠?guī)范好比基本的英語(yǔ)語(yǔ)法,這些語(yǔ)法教會(huì)了你怎么用一個(gè)個(gè)單詞拼湊出一句句話來(lái),而面向?qū)ο笠?guī)范教你怎么用類,怎么把一些屬性和方法封裝在一個(gè)類里,怎么串出類之間的繼承關(guān)系。
妻子: 啊哈,我知道了,那么,面向?qū)ο筮m用于哪里呢。
丈夫: 聽(tīng)我慢慢道來(lái)?,F(xiàn)在,假設(shè)你想寫點(diǎn)有內(nèi)容有題材的文章。你當(dāng)然還希望寫點(diǎn)你比較擅長(zhǎng)的題材的書,就會(huì)簡(jiǎn)單造幾個(gè)句子是遠(yuǎn)遠(yuǎn)不夠的,對(duì)吧。你需要筆耕不輟寫出一些長(zhǎng)篇大論,你還需要學(xué)習(xí)怎么可以讓讀者很容易就看懂你寫的這些長(zhǎng)篇大論。。。
妻子:嗯,有那么點(diǎn)意思。。。繼續(xù)吧
丈夫:現(xiàn)在,假如你想寫本關(guān)于面向?qū)ο笤O(shè)計(jì)的書,你需要把這個(gè)大的課題拆分成一些小題目。把這些小題目分幾個(gè)章節(jié)寫,還得寫前言,簡(jiǎn)介,說(shuō)明,舉例,一篇里還有很多段落。你需要設(shè)計(jì)一整本書,還得練習(xí)一些寫作技巧,讓文章讀起來(lái)淺顯易懂。這就是綜觀全局。
在軟件開(kāi)發(fā)中,OOD就是用來(lái)解決從全局出發(fā)考慮問(wèn)題,在設(shè)計(jì)軟件的時(shí)候,類和代碼可以模塊化,可重復(fù)使用,可靈活應(yīng)用,現(xiàn)在已經(jīng)有很多前人總結(jié)出的類和對(duì)象的設(shè)計(jì)原理了,我們直接拿來(lái)用就行了,總之,歷史的車輪已經(jīng)碾壓出一條清晰的車輪印,我們只要照著走就可以了。
妻子: 哎,懂了點(diǎn)皮毛,還有很多要學(xué)呢。
丈夫:不用擔(dān)心,你很快就會(huì)上手的,讓我們接著來(lái)吧。
#p#
話題:為什么要進(jìn)行面向?qū)ο笤O(shè)計(jì)?
作者:有個(gè)很重要的問(wèn)題,既然我們能夠很快的創(chuàng)建幾個(gè)類,編寫程序并提交,為什么我們還要關(guān)注面向?qū)ο笤O(shè)計(jì)?這樣不夠么?
妻子:恩,以前我不知道面向?qū)ο笤O(shè)計(jì),我也能開(kāi)發(fā)提交項(xiàng)目。有什么關(guān)系?
丈夫:好吧,先讓我給你看一個(gè)經(jīng)典的引述:
"需求不變的程序開(kāi)發(fā)會(huì)同行走在冰上一樣簡(jiǎn)單。" - Edward V. Berard
妻子:你是指軟件開(kāi)發(fā)說(shuō)明書會(huì)被不斷修改?
丈夫:非常正確!軟件開(kāi)發(fā)唯一的真理是“軟件必然修改”。為什么?
要知道,你的軟件解決的是現(xiàn)實(shí)世界中的問(wèn)題,而現(xiàn)實(shí)生活不是一成不變的。
可能你的軟件現(xiàn)在運(yùn)行良好。但它能靈活的支持“變化”嗎?如果不能,那它就不是一個(gè)敏捷設(shè)計(jì)的軟件。
妻子:好,那你就解釋一下什么叫做“敏捷設(shè)計(jì)的軟件”!
丈夫:“一個(gè)敏捷設(shè)計(jì)的軟件能輕松應(yīng)對(duì)變化,能被擴(kuò)展和復(fù)用。”
而應(yīng)用“面向?qū)ο笤O(shè)計(jì)”是做到敏捷設(shè)計(jì)的關(guān)鍵。那么,什么時(shí)候你可以說(shuō)你的程序應(yīng)用了面向?qū)ο笤O(shè)計(jì)?
妻子:我也正想問(wèn)呢。
丈夫:如果代碼符合以下幾點(diǎn),那么你就在“面向?qū)ο笤O(shè)計(jì)”:
面向?qū)ο螅粡?fù)用;變化的代價(jià)極小;無(wú)需改代碼即可擴(kuò)展
妻子:然后呢?
丈夫:不只我們。很多人也花了很多時(shí)間和精力思考這個(gè)問(wèn)題上,他們嘗試更好的進(jìn)行“面向?qū)ο笤O(shè)計(jì)”,并為“面向?qū)ο笤O(shè)計(jì)”指出幾條基本的原則(你可以用在你的“面向?qū)ο笤O(shè)計(jì)”中)。他們也確實(shí)總結(jié)出了一些通用的設(shè)計(jì)模式(基于基本的原則)。
妻子:你能說(shuō)出一些嗎?
丈夫:沒(méi)問(wèn)題?,F(xiàn)在有許多設(shè)計(jì)原則,但是最基本的,就是SOLID(縮寫),這五項(xiàng)原則。(感謝鮑勃叔叔,偉大OOD導(dǎo)師)。
S = 單一責(zé)任原則
O = 開(kāi)閉原則
L = Liscov替換原則
I = 接口隔離原則
D = 依賴倒置原則
在下面的討論中,我們將詳細(xì)了解這些。
#p#
話題:?jiǎn)我还δ茉瓌t
作者:讓我們先來(lái)看圖,我們應(yīng)該感謝制作這張圖的人,因?yàn)樗鼈冋娴奶腥ち恕?/p>
單一功能原則圖
它的意思是:“如果你可以在一個(gè)設(shè)備中實(shí)現(xiàn)所有的功能,你卻不能這樣做”。為什么呢?因?yàn)閺拈L(zhǎng)遠(yuǎn)來(lái)看它增加了很多的可管理性問(wèn)題。
從面向?qū)ο蠼嵌冉忉屖牵?/p>
"導(dǎo)致類變化的因素永遠(yuǎn)不要多于一個(gè)。"
或者換行個(gè)說(shuō)法:"一個(gè)類有且只有一個(gè)職責(zé)"。
妻子:可以解釋一下么?
丈夫:當(dāng)然,這個(gè)原則是說(shuō),如果有多于一個(gè)原因會(huì)導(dǎo)致你的類改變(或者它的職責(zé)多余一個(gè)),你就需要根據(jù)其職責(zé)把這個(gè)類拆分為多個(gè)類。
妻子:嗯...這是不是意味著在一個(gè)類里不能有多個(gè)方法?
丈夫:當(dāng)然不是。你當(dāng)然可以在一個(gè)類中包含多個(gè)方法。問(wèn)題是,他們都是為了一個(gè)目的。那么,為什么拆分很重要的?
那是因?yàn)椋?/p>
每個(gè)職責(zé)都是軸向變化;
如果類包含多個(gè)職責(zé),代碼會(huì)變得耦合;
妻子:給個(gè)例子唄?
丈夫:木有問(wèn)題啊,瞅瞅下面類的結(jié)構(gòu)。其實(shí),這個(gè)例子是 Bob 叔叔那兒來(lái)的,得謝謝他。
違反SRP原則的類層次結(jié)構(gòu)
這里,Rectangle 類干了下面兩件事:
計(jì)算矩形面積; 在界面上繪制矩形;
而且,有兩個(gè)程序使用了 Rectangle 類:
計(jì)算幾何應(yīng)用程序用這個(gè)類計(jì)算面積; 圖形程序用這個(gè)類在界面上繪制矩形;
這違反了SRP原則(單一職責(zé)原則)!
妻子:腫么回事?
丈夫:你瞅瞅,Rectangle 類干了倆不相干的事。一個(gè)方法它計(jì)算了面積,另外一個(gè)它返回一個(gè)表示矩形的 GUI 資源。這問(wèn)題就有點(diǎn)樂(lè)了:
在計(jì)算幾何應(yīng)用程序里咱得包著 GUI。就是說(shuō),寫幾何應(yīng)用代碼,咱也得引用 GUI 庫(kù);
要是為了圖形應(yīng)用所改變 Rectangle 類,計(jì)算幾何應(yīng)用也可能跟著變,然后還得編譯,還得測(cè)試,另一邊也是;
妻子:是很樂(lè)。就是說(shuō),咱得根據(jù)累的職責(zé)分開(kāi)寫唄?
丈夫:必須滴。猜猜怎么干?
妻子:我想想,我尋思這得這么辦:
我瞅著得按職責(zé)拆成兩個(gè)類:
Rectangle:這個(gè)類定義 Area() 方法;
RectangleUI:這個(gè)把 Rectangle 類繼承過(guò)來(lái),定義 Draw() 方法。
丈夫:很好。這么個(gè),計(jì)算幾何應(yīng)用使 Rectangle 類,圖形應(yīng)用使 RectangleUI 類。咱還可以把這倆類分到倆單獨(dú)的 DLL 中,然后改的時(shí)候就不用管另一個(gè)了。
妻子:謝了,我大概明白 SRP 原則了一句話:SPR 就是把東西分到不能再分了,再集中化管理和復(fù)用。囔,在方法層面上,咱不也得用 SPR 原則?我是說(shuō),咱寫的方法里有很多干不同事兒的代碼,這也不符合 SPR原則吧。
丈夫:你說(shuō)地不差。方法也得分開(kāi),一個(gè)方法干一個(gè)活。這么著你復(fù)用方法,要是改了,也不用改太多。
#p#
話題:開(kāi)閉原則
作者:“開(kāi)閉原則“圖示如下:
圖:開(kāi)閉原則圖
讓我來(lái)解釋一下,設(shè)計(jì)規(guī)則如下:
“軟件實(shí)體(類,模塊,函數(shù)等)應(yīng)該對(duì)擴(kuò)展開(kāi)放,對(duì)修改關(guān)閉。”
這意味著在最基本的層面上,你可以擴(kuò)展一個(gè)類的行為,而無(wú)需修改。這就像我能夠穿上衣服,而對(duì)我的身體不做任何改變,哈哈。
妻子: 太有意思啦. 你可以通過(guò)穿不同的衣服來(lái)改變你的外貌, 但是你不必為此改變自己的身體.所以你是對(duì)擴(kuò)展開(kāi)放的, 對(duì)吧?
丈夫: 是的. 在面向?qū)ο笤O(shè)計(jì)中, 對(duì)擴(kuò)展開(kāi)放意味著模塊/類的行為可以被擴(kuò)展,那么當(dāng)需求變化時(shí)我們可以用各種各樣的方法制定功能來(lái)滿足需求變更或者新需求
妻子: 除此之外你的身體是對(duì)修改關(guān)閉的. 我喜歡這個(gè)例子. 所以, 對(duì)于核心模塊或類的代碼在需要擴(kuò)展的時(shí)候不應(yīng)該被修改. 你能結(jié)合具體例子解釋下嗎?
丈夫: 當(dāng)然了, 先看下面的例子.這個(gè)就不支持 "開(kāi)放-關(guān)閉" 原則:
類的層次結(jié)構(gòu)已經(jīng)表明了這是違反"開(kāi)放-關(guān)閉"原則的.
你看, 客戶端類和服務(wù)端類都是具體的實(shí)現(xiàn)類. 因?yàn)? 如果某些原因?qū)е路?wù)端實(shí)現(xiàn)改變了, 客戶端也需要相應(yīng)變化.
妻子: 有道理. 如果一個(gè)瀏覽器的實(shí)現(xiàn)和一個(gè)指定的服務(wù)器(比如IIS)緊緊的耦合在一起 , 那么如果服務(wù)器由于某種原因替換成了另外的 (比如, Apache) 瀏覽器也需要做相應(yīng)的變化或者被替換掉. 多么恐怖的一件事啊!
丈夫: 非常正確. 因?yàn)橄旅娴膶⑹且环N好的設(shè)計(jì)方案:
類的層次關(guān)系展示了"開(kāi)放-關(guān)閉"原則
在這個(gè)例子中, 添加了一個(gè)抽象的Server類, 并且客戶端保持了抽象類的引用, 具體的Server類實(shí)現(xiàn)了這個(gè)抽象Server類. 所以, 由于某種原因Server的實(shí)現(xiàn)類發(fā)生了改變, 客戶端不需要做任何改變.
這里的抽象的Server類對(duì)修改關(guān)閉, 具體的Server實(shí)現(xiàn)類對(duì)擴(kuò)展開(kāi)放.
妻子: 我的理解是, 抽象是關(guān)鍵, 對(duì)嗎?
丈夫: 是的, 基本上, 你要對(duì)系統(tǒng)的核心業(yè)務(wù)進(jìn)行抽象, 如果你抽象化做的比較好, 很可能, 在擴(kuò)展功能的時(shí)候它們不必做任何改變 (比如Server就是一個(gè)抽象的概念). 你所定義的抽象的實(shí)現(xiàn) (比如, IIS服務(wù)器 實(shí)現(xiàn)了 Server) 和 抽象的代碼 (Server) 要盡可能的多. 這樣在客戶端代碼中不需要做任何修改就會(huì)允許你定義一個(gè)新的實(shí)現(xiàn)(比如, ApacheServer) .
#p#
主題: 里氏替換原則
丈夫: "里氏替換原則"聽(tīng)起來(lái)非常的復(fù)雜,但是思想史非?;A(chǔ)的. 看下面這個(gè)有趣的海報(bào)
里氏替換原則海報(bào)
原則描述了:
"子類型必須能夠替換它們的基類."
或者, 換句話說(shuō):
"使用基類引用的函數(shù)必須能夠使用派生類而無(wú)須了解派生類."
妻子: 對(duì)不起, 這聽(tīng)起來(lái)讓我覺(jué)得有點(diǎn)亂. 我認(rèn)為這個(gè)是面向?qū)ο缶幊痰幕驹瓌t. 這個(gè)叫做多態(tài)性, 對(duì)吧? 為什么面向?qū)ο笤O(shè)計(jì)原則需要考慮這個(gè)問(wèn)題?
丈夫: 非常好的問(wèn)題. 這有一些答案:
在基本的面向?qū)ο笤瓌t中, "繼承" 通常被描述成 "is a" 的關(guān)系. 如果一個(gè) "開(kāi)發(fā)者" 是"軟件專業(yè)人員", 那么 "開(kāi)發(fā)者" 類 應(yīng)該 繼承 "軟件開(kāi)發(fā)人員" 類. 這樣的 "Is a" 關(guān)系 在類設(shè)計(jì)階段非常重要, 但是這也很容易讓設(shè)計(jì)者得意忘形從而以一個(gè)糟糕的繼承設(shè)計(jì)告終.
"里氏替換原則" 僅僅是一種確保繼承被正確使用的手段.
妻子:我明白了。真有趣。
丈夫:是的,親愛(ài)的,確實(shí)如此。讓我們來(lái)看看一個(gè)例子:
類層次結(jié)構(gòu)圖展示的是一個(gè)Liskov替換原則的例子.因?yàn)?KingFisher類拓展(繼承)了Bird類,因此繼承了Fly()這個(gè)方法,這是非常不錯(cuò)的.
我們?cè)賮?lái)看看下面的例子
修正過(guò)的Liskov替換原則的類層次結(jié)構(gòu)圖
Ostrich(鴕鳥(niǎo))是一種鳥(niǎo)(顯然是),并繼承了 Bird 類。但它能飛嗎?不能,這個(gè)設(shè)計(jì)就違反了里氏替換原則。
因此,即使在現(xiàn)實(shí)中看上去沒(méi)什么問(wèn)題,在類設(shè)計(jì)中,Ostrich 都不應(yīng)該繼承 Bird 類,而應(yīng)該從 Bird 中分出一個(gè)不會(huì)飛的類,由 Ostrich 繼承。
妻子:好吧,明白了。我說(shuō)說(shuō)為什么里氏替換原則如此重要:
如果不遵循 LSP原則,類繼承就會(huì)混亂。如果子類實(shí)例被作為參數(shù)傳遞給方法,后果難以預(yù)測(cè)。
如果不遵循 LSP原則,基于父類編寫的單元測(cè)試代碼將無(wú)法成功運(yùn)行子類。
我說(shuō)的對(duì)嗎?
作者:完全正確,你可以設(shè)計(jì)一個(gè)對(duì)象并用LSP作為驗(yàn)證工具來(lái)測(cè)試該對(duì)象是否能夠繼承。
#p#
話題:接口隔離原則
作者:今天我們講下“接口隔離原則”,看看下面這張海報(bào)
接口隔離原則海報(bào)
妻子:這是什么意思?
作者:它的意思是這樣的:“用戶不應(yīng)該被迫依賴他們不使用的接口。”
妻子:解釋一下。
作者:好吧,解釋如下:
假設(shè)你想去買一臺(tái)電視機(jī)并且有兩種類型可以選擇,其中一種有很多開(kāi)關(guān)和按鈕,但是多數(shù)對(duì)你來(lái)說(shuō)用不到,另一種只有幾個(gè)開(kāi)關(guān)和按鈕,并且看來(lái)你很熟悉怎么用。如果這兩種電視機(jī)提供同樣的功能,你會(huì)選擇哪一種?
妻子:當(dāng)然是第二種了。
作者:嗯,但是為什么呢?
妻子:因?yàn)槲也恍枰雌饋?lái)很麻煩而且對(duì)我也不必要的開(kāi)關(guān)和按鈕。
丈夫:正確。同樣的,假如你有一些類,你通過(guò)接口暴露了類的功能,這樣外部就能夠知道類中可用的功能,客戶端也可以根據(jù)接口來(lái)設(shè)計(jì)。當(dāng)然那,如果接口太大,或是暴露的方法太多,從外部看也會(huì)很混亂。接口包含的方法太多也會(huì)降低可復(fù)用性, 這種包含無(wú)用方法的”胖接口“無(wú)疑會(huì)增加類的耦合。
這還會(huì)引起其他的問(wèn)題。如果一個(gè)類視圖實(shí)現(xiàn)接口,它需要實(shí)現(xiàn)接口中所有的方法,哪怕一點(diǎn)都用不到。所以,這樣會(huì)增加系統(tǒng)復(fù)雜度,降低系統(tǒng)可維護(hù)性和穩(wěn)定性。
接口隔離原則確保接口實(shí)現(xiàn)自己的職責(zé),且清晰明確,易于理解,具有可復(fù)用性。
妻子:我明白了,你的意思是接口只應(yīng)該包括必要的方法而不是所有的。
作者:是的,讓我們看一個(gè)例子。
下面的接口是一個(gè)“胖接口”,這違反接口隔離原則:
違反接口隔離原則的接口示例
注意,IBird接口定義 Fly()的行為有許多鳥(niǎo)類的行為?,F(xiàn)在,如果一只鳥(niǎo)類(比方說(shuō),鴕鳥(niǎo))實(shí)現(xiàn)了這個(gè)接口,它將會(huì)實(shí)現(xiàn)不必要的 Fly()的行為(鴕鳥(niǎo)不會(huì)飛)。
妻子:是啊。因此,這個(gè)接口必須被分割?
作者:是的,“胖接口”應(yīng)該分隔成兩個(gè)不同的接口,IBird 和IFlyingBird,而IFlyingBird繼承于IBird。
接口隔離原則的例子中正確版本的接口
如果有一只不會(huì)飛的鳥(niǎo)(比如,駝鳥(niǎo)),只要用IBird接口即可,如果有一保會(huì)飛的鳥(niǎo)(比如,翠鳥(niǎo)),只要用IFlyingBird接口即可。
妻子:所以,回過(guò)頭來(lái)看有很多按鈕開(kāi)關(guān)的電視的例子,制造商應(yīng)該有電視機(jī)的圖紙,開(kāi)關(guān)和按鈕也在這個(gè)方案里。若他們想造一臺(tái)新款電視機(jī)時(shí)想要復(fù)用這張圖紙,他們必須添加更多的按鈕和開(kāi)關(guān),否則沒(méi)法復(fù)用,對(duì)么?
丈夫:對(duì)。
妻子:若是他們真的想要復(fù)用這個(gè)方案,他們應(yīng)該將電視機(jī)的圖紙分為更小的部分,才能在以后制造新款電視機(jī)的時(shí)候復(fù)用這些設(shè)計(jì)方案。
丈夫:你理解了。
#p#
話題:依賴倒置原則
作者:這是SOLID原則中最后的原則。圖示如下:
依賴倒置原則圖示
它的意思是:
“高層次的模塊不應(yīng)該依賴于低層次的模塊,而是,都應(yīng)該依賴于抽象。”
作者:我們用一個(gè)現(xiàn)實(shí)的例子來(lái)理解。你的汽車是用很多部件組成,比如發(fā)動(dòng)機(jī),車輪,空調(diào)和其他的部件,是吧?
妻子:是啊,當(dāng)然是這樣。
丈夫:你看,它們并沒(méi)有嚴(yán)格的構(gòu)建在一個(gè)部件里;就是說(shuō),它們都是“插件”,要是引擎或著車輪出了問(wèn)題,你可以單獨(dú)修理它,甚至換一個(gè)用。
替換時(shí),你只需要保證沉淪符合汽車的設(shè)計(jì)(汽車能使用任何1500CC的引擎或任何18寸的車輪)。
當(dāng)然,你可以在1500CC 的位置上安裝2000 CC的引擎,對(duì)某些制造商都一樣(豐田汽車)。
可如果你的汽車部件不是“可拔插”的呢?
妻子:那太可怕了!這樣的話,要是汽車引擎故障,你得整車修理,或者買一輛新車!
丈夫:是的,那么怎么做到"可插拔"呢?
妻子:關(guān)鍵是”抽象“,是吧?
丈夫:對(duì)?,F(xiàn)實(shí)世界中,汽車是高層級(jí)的模塊/實(shí)體,它依賴于底層級(jí)的模塊/實(shí)體,例如引擎和輪子。
相較于直接依賴于實(shí)體的引擎或輪子,汽車應(yīng)該依賴于抽象的引擎或輪子的規(guī)格,這樣只要是符合這個(gè)抽象規(guī)格的引擎或輪子,都可以裝到車?yán)锱堋?/p>
來(lái)看看下面的圖:
依賴倒置原則的類層次結(jié)構(gòu)
丈夫:注意上面的 Car類,它有兩個(gè)屬性,且都是抽象類型(接口)而非實(shí)體的。
引擎和車輪是可插拔的,這樣汽車能接受任何實(shí)現(xiàn)了聲明接口的對(duì)象,且 Car 類無(wú)需任何改動(dòng)。
妻子:所以,如果代碼不遵循依賴倒置,就有下面的風(fēng)險(xiǎn):
使用低層級(jí)類會(huì)破環(huán)高層級(jí)代碼;
當(dāng)?shù)蛯蛹?jí)的類變化時(shí),需要太多時(shí)間和代價(jià)來(lái)修改高層級(jí)代碼;
代碼可復(fù)用性不高
丈夫:親愛(ài)的,你說(shuō)到點(diǎn)子上了!
總結(jié)
丈夫:除 SOLID 原則外還有很多別的面向?qū)ο笤瓌t。比如:
“組合替代繼承”:是說(shuō)“用組合比用繼承好”;
“笛米特法則”:是說(shuō)“類對(duì)其它類知道的越少越好”;
“共同封閉原則”:是說(shuō)“相關(guān)類應(yīng)該一起打包”;
“穩(wěn)定抽象原則”:這是說(shuō)"類越穩(wěn)定,就越應(yīng)該是抽象類";
妻子:我得學(xué)習(xí)這些原則嗎?
丈夫:當(dāng)然了。你可以在網(wǎng)上學(xué)習(xí)。Google 它,學(xué)習(xí)它,理解它。有問(wèn)題就找我。
妻子:我聽(tīng)說(shuō)還有些根據(jù)設(shè)計(jì)原則編寫的設(shè)計(jì)模式。
丈夫:對(duì)的。設(shè)計(jì)模式不過(guò)就是針對(duì)一些經(jīng)常出現(xiàn)的場(chǎng)景的一些通用的設(shè)計(jì)建議。主要的想法還是面向?qū)ο笤瓌t。你可以認(rèn)為設(shè)計(jì)模式是“框架”,OOD 原則是“規(guī)范”。
妻子:那么之后我將學(xué)習(xí)設(shè)計(jì)模式是吧?
丈夫:是的,親愛(ài)的。
妻子:應(yīng)該會(huì)很有意思。
丈夫:必須地!
原文鏈接:http://www.oschina.net/translate/how-i-explained-ood-to-my-wife