我是怎樣教媳婦面向?qū)ο缶幊痰摹巨D(zhuǎn)】
當(dāng)前位置:點(diǎn)晴教程→知識(shí)管理交流
→『 技術(shù)文檔交流 』
簡(jiǎn)介我老婆 Farhana 想要繼續(xù)軟件開發(fā)生涯(之前因?yàn)槲覀兊牡谝粋€(gè)孩子出生,她不得不放棄)。我已經(jīng)有了一些軟件設(shè)計(jì)和開發(fā)的經(jīng)驗(yàn),所以這幾天我就在試著幫助她學(xué)習(xí)OOD。 由于我早年在軟件開發(fā)的經(jīng)驗(yàn),我總是發(fā)現(xiàn)無(wú)論一個(gè)技術(shù)問題看上去多么難搞,只要從現(xiàn)實(shí)生活的角度去解釋或用對(duì)話的方式去討論總能讓它變得更簡(jiǎn)單。關(guān)于OOD,我們已經(jīng)有了許多成果豐碩的討論,我覺得有人可能發(fā)現(xiàn)這是一個(gè)學(xué)習(xí)OOD有趣的方式,所以我想我應(yīng)該分享出來。 下面是我們的談話步驟: 話題:介紹面向?qū)ο笤O(shè)計(jì) 丈夫:親愛的,讓我們開始學(xué)習(xí)面向?qū)ο笤O(shè)計(jì)。你了解面向?qū)ο笠?guī)范,對(duì)嗎? 妻子:你是指封裝,繼承和多態(tài)嗎?是的,我了解這些規(guī)范。 丈夫:行,我想你已經(jīng)知道怎么用類和對(duì)象了。今天我們來學(xué)習(xí)面向?qū)ο笤O(shè)計(jì)。 妻子:等等。了解面向?qū)ο笠?guī)范對(duì)面向?qū)ο缶幊虂碚f難道不夠嗎?我的意思是,我能夠定義類,封裝屬性和方法。我能夠根據(jù)它們的關(guān)系定義類的繼承。那還有什么呢? 丈夫:很好的問題。面向?qū)ο笠?guī)范和面向?qū)ο缶幊掏耆莾纱a事。讓我展示一個(gè)現(xiàn)實(shí)生活中的例子來幫助你理解它們。 我們從牙牙學(xué)語(yǔ)起,都是先從字母表學(xué)起的,對(duì)吧? 妻子: 嗯。 丈夫: 好,然后你就能認(rèn)單詞了,還能通過不同的字母拼寫出不同的單詞來。慢慢的,你能通過一些基本的語(yǔ)法把這些單詞串成一句話。為了使句子時(shí)態(tài)正確且沒有語(yǔ)病,你需要用一些介詞,連詞,等等。??聪旅孢@句話 "I" (代詞) "want" (動(dòng)詞) "to" (介詞) "learn" (動(dòng)詞) "OOD" (名詞) 通過把幾個(gè)單詞擺放妥當(dāng)一句話就好了,然后用個(gè)關(guān)鍵詞來說明一下這句話的重點(diǎn)。 妻子: 親愛的,你閑扯這些到底要說明什么呢 丈夫: 我說的這個(gè)例子跟面向?qū)ο笠?guī)范很類似,面向?qū)ο笠?guī)范為面向?qū)ο缶幊潭x了基本的規(guī)范,它是面向?qū)ο缶幊痰闹饕枷?。面向?qū)ο笠?guī)范好比基本的英語(yǔ)語(yǔ)法,這些語(yǔ)法教會(huì)了你怎么用一個(gè)個(gè)單詞拼湊出一句句話來,而面向?qū)ο笠?guī)范教你怎么用類,怎么把一些屬性和方法封裝在一個(gè)類里,怎么串出類之間的繼承關(guān)系。 妻子: 啊哈,我知道了,那么,面向?qū)ο筮m用于哪里呢。 丈夫: 聽我慢慢道來?,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)介,說明,舉例,一篇里還有很多段落。你需要設(shè)計(jì)一整本書,還得練習(xí)一些寫作技巧,讓文章讀起來淺顯易懂。這就是綜觀全局。 在軟件開發(fā)中,OOD就是用來解決從全局出發(fā)考慮問題,在設(shè)計(jì)軟件的時(shí)候,類和代碼可以模塊化,可重復(fù)使用,可靈活應(yīng)用,現(xiàn)在已經(jīng)有很多前人總結(jié)出的類和對(duì)象的設(shè)計(jì)原理了,我們直接拿來用就行了,總之,歷史的車輪已經(jīng)碾壓出一條清晰的車輪印,我們只要照著走就可以了。 妻子: 哎,懂了點(diǎn)皮毛,還有很多要學(xué)呢。 丈夫:不用擔(dān)心,你很快就會(huì)上手的,讓我們接著來吧。 話題:為什么要進(jìn)行面向?qū)ο笤O(shè)計(jì)?作者:有個(gè)很重要的問題,既然我們能夠很快的創(chuàng)建幾個(gè)類,編寫程序并提交,為什么我們還要關(guān)注面向?qū)ο笤O(shè)計(jì)?這樣不夠么? 妻子:恩,以前我不知道面向?qū)ο笤O(shè)計(jì),我也能開發(fā)提交項(xiàng)目。有什么關(guān)系? 丈夫:好吧,先讓我給你看一個(gè)經(jīng)典的引述:
妻子:你是指軟件開發(fā)說明書會(huì)被不斷修改? 丈夫:非常正確!軟件開發(fā)唯一的真理是“軟件必然修改”。為什么? 要知道,你的軟件解決的是現(xiàn)實(shí)世界中的問題,而現(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í)候你可以說你的程序應(yīng)用了面向?qū)ο笤O(shè)計(jì)? 妻子:我也正想問呢。 丈夫:如果代碼符合以下幾點(diǎn),那么你就在“面向?qū)ο笤O(shè)計(jì)”:
妻子:然后呢? 丈夫:不只我們。很多人也花了很多時(shí)間和精力思考這個(gè)問題上,他們嘗試更好的進(jìn)行“面向?qū)ο笤O(shè)計(jì)”,并為“面向?qū)ο笤O(shè)計(jì)”指出幾條基本的原則(你可以用在你的“面向?qū)ο笤O(shè)計(jì)”中)。他們也確實(shí)總結(jié)出了一些通用的設(shè)計(jì)模式(基于基本的原則)。 妻子:你能說出一些嗎? 丈夫:沒問題?,F(xiàn)在有許多設(shè)計(jì)原則,但是最基本的,就是SOLID(縮寫),這五項(xiàng)原則。(感謝鮑勃叔叔,偉大OOD導(dǎo)師)。 S = 單一責(zé)任原則 在下面的討論中,我們將詳細(xì)了解這些。 話題:?jiǎn)我还δ茉瓌t作者:讓我們先來看圖,我們應(yīng)該感謝制作這張圖的人,因?yàn)樗鼈冋娴奶腥ち恕?
單一功能原則圖 它的意思是:“如果你可以在一個(gè)設(shè)備中實(shí)現(xiàn)所有的功能,你卻不能這樣做”。為什么呢?因?yàn)?SPAN style="LINE-HEIGHT: 1.5; FONT-SIZE: 14px">從長(zhǎng)遠(yuǎn)來看它增加了很多的可管理性問題。 從面向?qū)ο蠼嵌冉忉屖牵? "導(dǎo)致類變化的因素永遠(yuǎn)不要多于一個(gè)。" 或者換行個(gè)說法:"一個(gè)類有且只有一個(gè)職責(zé)"。 妻子:可以解釋一下么? 丈夫:當(dāng)然,這個(gè)原則是說,如果有多于一個(gè)原因會(huì)導(dǎo)致你的類改變(或者它的職責(zé)多余一個(gè)),你就需要根據(jù)其職責(zé)把這個(gè)類拆分為多個(gè)類。 妻子:嗯...這是不是意味著在一個(gè)類里不能有多個(gè)方法? 丈夫:當(dāng)然不是。你當(dāng)然可以在一個(gè)類中包含多個(gè)方法。問題是,他們都是為了一個(gè)目的。那么,為什么拆分很重要的? 那是因?yàn)椋?
妻子:給個(gè)例子唄? 丈夫:木有問題啊,瞅瞅下面類的結(jié)構(gòu)。其實(shí),這個(gè)例子是 Bob 叔叔那兒來的,得謝謝他。
違反SRP原則的類層次結(jié)構(gòu) 這里,Rectangle 類干了下面兩件事:
而且,有兩個(gè)程序使用了 Rectangle 類:
這違反了SRP原則(單一職責(zé)原則)! 妻子:腫么回事? 丈夫:你瞅瞅,Rectangle 類干了倆不相干的事。一個(gè)方法它計(jì)算了面積,另外一個(gè)它返回一個(gè)表示矩形的 GUI 資源。這問題就有點(diǎn)樂了:
妻子:是很樂。就是說,咱得根據(jù)類的職責(zé)分開寫唄? 丈夫:必須滴。猜猜怎么干? 妻子:我想想,我尋思這得這么辦: 我瞅著得按職責(zé)拆成兩個(gè)類:
丈夫:很好。這么個(gè),計(jì)算幾何應(yīng)用使 Rectangle 類,圖形應(yīng)用使 RectangleUI 類。咱還可以把這倆類分到倆單獨(dú)的 DLL 中,然后改的時(shí)候就不用管另一個(gè)了。 妻子:謝了,我大概明白 SRP 原則了一句話:SPR 就是把東西分到不能再分了,再集中化管理和復(fù)用。囔,在方法層面上,咱不也得用 SPR 原則?我是說,咱寫的方法里有很多干不同事兒的代碼,這也不符合 SPR原則吧。 丈夫:你說地不差。方法也得分開,一個(gè)方法干一個(gè)活。這么著你復(fù)用方法,要是改了,也不用改太多。 話題:開閉原則作者:“開閉原則“圖示如下:
圖:開閉原則圖 讓我來解釋一下,設(shè)計(jì)規(guī)則如下: “軟件實(shí)體(類,模塊,函數(shù)等)應(yīng)該對(duì)擴(kuò)展開放,對(duì)修改關(guān)閉。” 這意味著在最基本的層面上,你可以擴(kuò)展一個(gè)類的行為,而無(wú)需修改。這就像我能夠穿上衣服,而對(duì)我的身體不做任何改變,哈哈。 妻子: 太有意思啦. 你可以通過穿不同的衣服來改變你的外貌, 但是你不必為此改變自己的身體.所以你是對(duì)擴(kuò)展開放的, 對(duì)吧? 丈夫: 是的. 在面向?qū)ο笤O(shè)計(jì)中, 對(duì)擴(kuò)展開放意味著模塊/類的行為可以被擴(kuò)展,那么當(dāng)需求變化時(shí)我們可以用各種各樣的方法制定功能來滿足需求變更或者新需求 妻子: 除此之外你的身體是對(duì)修改關(guān)閉的. 我喜歡這個(gè)例子. 所以, 對(duì)于核心模塊或類的代碼在需要擴(kuò)展的時(shí)候不應(yīng)該被修改. 你能結(jié)合具體例子解釋下嗎? 丈夫: 當(dāng)然了, 先看下面的例子.這個(gè)就不支持 "開放-關(guān)閉" 原則:
類的層次結(jié)構(gòu)已經(jīng)表明了這是違反"開放-關(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)系展示了"開放-關(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ò)展開放. 妻子: 我的理解是, 抽象是關(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) . 主題: 里氏替換原則丈夫: "里氏替換原則"聽起來非常的復(fù)雜,但是設(shè)計(jì)思想?yún)s是非?;A(chǔ)的. 看下面這個(gè)有趣的海報(bào)
里氏替換原則海報(bào) 原則描述了: "子類型必須能夠替換它們的基類." 或者, 換句話說: "使用基類引用的函數(shù)必須能夠使用派生類而無(wú)須了解派生類." 妻子: 對(duì)不起, 這聽起來讓我覺得有點(diǎn)亂. 我認(rèn)為這個(gè)是面向?qū)ο缶幊痰幕驹瓌t. 這個(gè)叫做多態(tài)性, 對(duì)吧? 為什么面向?qū)ο笤O(shè)計(jì)原則需要考慮這個(gè)問題? 丈夫: 非常好的問題. 這有一些答案: 在基本的面向?qū)ο笤瓌t中, "繼承" 通常被描述成 "is a" 的關(guān)系. 如果一個(gè) "開發(fā)者" 是"軟件專業(yè)人員", 那么 "開發(fā)者" 類 應(yīng)該 繼承 "軟件開發(fā)人員" 類. 這樣的 "Is a" 關(guān)系 在類設(shè)計(jì)階段非常重要, 但是這也很容易讓設(shè)計(jì)者得意忘形從而以一個(gè)糟糕的繼承設(shè)計(jì)告終. "里氏替換原則" 僅僅是一種確保繼承被正確使用的手段. 妻子:我明白了。真有趣。 丈夫:是的,親愛的,確實(shí)如此。讓我們來看看一個(gè)例子:
類層次結(jié)構(gòu)圖展示的是一個(gè)Liskov替換原則的例子.因?yàn)?KingFisher類拓展(繼承)了Bird類,因此繼承了Fly()這個(gè)方法,這是非常不錯(cuò)的. 我們?cè)賮砜纯聪旅娴睦?
修正過的Liskov替換原則的類層次結(jié)構(gòu)圖 Ostrich(鴕鳥)是一種鳥(顯然是),并繼承了 Bird 類。但它能飛嗎?不能,這個(gè)設(shè)計(jì)就違反了里氏替換原則。 因此,即使在現(xiàn)實(shí)中看上去沒什么問題,在類設(shè)計(jì)中,Ostrich 都不應(yīng)該繼承 Bird 類,而應(yīng)該從 Bird 中分出一個(gè)不會(huì)飛的類,由 Ostrich 繼承。 妻子:好吧,明白了。我說說為什么里氏替換原則如此重要:
我說的對(duì)嗎? 作者:完全正確,你可以設(shè)計(jì)一個(gè)對(duì)象并用LSP作為驗(yàn)證工具來測(cè)試該對(duì)象是否能夠繼承。 話題:接口隔離原則作者:今天我們講下“接口隔離原則”,看看下面這張海報(bào)
接口隔離原則海報(bào) 妻子:這是什么意思? 作者:它的意思是這樣的:“用戶不應(yīng)該被迫依賴他們不使用的接口?!? 妻子:解釋一下。 作者:好吧,解釋如下: 假設(shè)你想去買一臺(tái)電視機(jī)并且有兩種類型可以選擇,其中一種有很多開關(guān)和按鈕,但是多數(shù)對(duì)你來說用不到,另一種只有幾個(gè)開關(guān)和按鈕,并且看來你很熟悉怎么用。如果這兩種電視機(jī)提供同樣的功能,你會(huì)選擇哪一種? 妻子:當(dāng)然是第二種了。 作者:嗯,但是為什么呢? 妻子:因?yàn)槲也恍枰雌饋砗苈闊┒覍?duì)我也不必要的開關(guān)和按鈕。 丈夫:正確。同樣的,假如你有一些類,你通過接口暴露了類的功能,這樣外部就能夠知道類中可用的功能,客戶端也可以根據(jù)接口來設(shè)計(jì)。當(dāng)然那,如果接口太大,或是暴露的方法太多,從外部看也會(huì)很混亂。接口包含的方法太多也會(huì)降低可復(fù)用性, 這種包含無(wú)用方法的”胖接口“無(wú)疑會(huì)增加類的耦合。 這還會(huì)引起其他的問題。如果一個(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()的行為有許多鳥類的行為?,F(xiàn)在,如果一只鳥類(比方說,鴕鳥)實(shí)現(xiàn)了這個(gè)接口,它將會(huì)實(shí)現(xiàn)不必要的 Fly()的行為(鴕鳥不會(huì)飛)。 妻子:是啊。因此,這個(gè)接口必須被分割? 作者:是的,“胖接口”應(yīng)該分隔成兩個(gè)不同的接口,IBird 和IFlyingBird,而IFlyingBird繼承于IBird。
接口隔離原則的例子中正確版本的接口 如果有一只不會(huì)飛的鳥(比如,駝鳥),只要用IBird接口即可,如果有一保會(huì)飛的鳥(比如,翠鳥),只要用IFlyingBird接口即可。 妻子:所以,回過頭來看有很多按鈕開關(guān)的電視的例子,制造商應(yīng)該有電視機(jī)的圖紙,開關(guān)和按鈕也在這個(gè)方案里。若他們想造一臺(tái)新款電視機(jī)時(shí)想要復(fù)用這張圖紙,他們必須添加更多的按鈕和開關(guān),否則沒法復(fù)用,對(duì)么? 丈夫:對(duì)。 妻子:若是他們真的想要復(fù)用這個(gè)方案,他們應(yīng)該將電視機(jī)的圖紙分為更小的部分,才能在以后制造新款電視機(jī)的時(shí)候復(fù)用這些設(shè)計(jì)方案。 丈夫:你理解了。 話題:依賴倒置原則作者:這是SOLID原則中最后的原則。圖示如下:
依賴倒置原則圖示 它的意思是: “高層次的模塊不應(yīng)該依賴于低層次的模塊,而是,都應(yīng)該依賴于抽象?!? 作者:我們用一個(gè)現(xiàn)實(shí)的例子來理解。你的汽車是用很多部件組成,比如發(fā)動(dòng)機(jī),車輪,空調(diào)和其他的部件,是吧? 妻子:是啊,當(dāng)然是這樣。 丈夫:你看,它們并沒有嚴(yán)格的構(gòu)建在一個(gè)部件里;就是說,它們都是“插件”,要是引擎或著車輪出了問題,你可以單獨(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)锱堋? 來看看下面的圖:
依賴倒置原則的類層次結(jié)構(gòu) 丈夫:注意上面的 Car類,它有兩個(gè)屬性,且都是抽象類型(接口)而非實(shí)體的。 引擎和車輪是可插拔的,這樣汽車能接受任何實(shí)現(xiàn)了聲明接口的對(duì)象,且 Car 類無(wú)需任何改動(dòng)。 妻子:所以,如果代碼不遵循依賴倒置,就有下面的風(fēng)險(xiǎn):
丈夫:親愛的,你說到點(diǎn)子上了! 總結(jié)丈夫:除 SOLID 原則外還有很多別的面向?qū)ο笤瓌t。比如:
妻子:我得學(xué)習(xí)這些原則嗎? 丈夫:當(dāng)然了。你可以在網(wǎng)上學(xué)習(xí)。Google 它,學(xué)習(xí)它,理解它。有問題就找我。 妻子:我聽說還有些根據(jù)設(shè)計(jì)原則編寫的設(shè)計(jì)模式。 丈夫:對(duì)的。設(shè)計(jì)模式不過就是針對(duì)一些經(jīng)常出現(xiàn)的場(chǎng)景的一些通用的設(shè)計(jì)建議。主要的想法還是面向?qū)ο笤瓌t。你可以認(rèn)為設(shè)計(jì)模式是“框架”,OOD 原則是“規(guī)范”。 妻子:那么之后我將學(xué)習(xí)設(shè)計(jì)模式是吧? 丈夫:是的,親愛的。 妻子:應(yīng)該會(huì)很有意思。 丈夫:必須地! 本文中的所有譯文僅用于學(xué)習(xí)和交流目的,轉(zhuǎn)載請(qǐng)務(wù)必注明文章譯者、出處、和本文鏈接 該文章在 2013/1/20 0:21:59 編輯過 |
關(guān)鍵字查詢
相關(guān)文章
正在查詢... |