我是怎樣教媳婦面向對象編程的【轉】
當前位置:點晴教程→知識管理交流
→『 技術文檔交流 』
簡介我老婆 Farhana 想要繼續軟件開發生涯(之前因為我們的第一個孩子出生,她不得不放棄)。我已經有了一些軟件設計和開發的經驗,所以這幾天我就在試著幫助她學習OOD。 由于我早年在軟件開發的經驗,我總是發現無論一個技術問題看上去多么難搞,只要從現實生活的角度去解釋或用對話的方式去討論總能讓它變得更簡單。關于OOD,我們已經有了許多成果豐碩的討論,我覺得有人可能發現這是一個學習OOD有趣的方式,所以我想我應該分享出來。 下面是我們的談話步驟: 話題:介紹面向對象設計 丈夫:親愛的,讓我們開始學習面向對象設計。你了解面向對象規范,對嗎? 妻子:你是指封裝,繼承和多態嗎?是的,我了解這些規范。 丈夫:行,我想你已經知道怎么用類和對象了。今天我們來學習面向對象設計。 妻子:等等。了解面向對象規范對面向對象編程來說難道不夠嗎?我的意思是,我能夠定義類,封裝屬性和方法。我能夠根據它們的關系定義類的繼承。那還有什么呢? 丈夫:很好的問題。面向對象規范和面向對象編程完全是兩碼事。讓我展示一個現實生活中的例子來幫助你理解它們。 我們從牙牙學語起,都是先從字母表學起的,對吧? 妻子: 嗯。 丈夫: 好,然后你就能認單詞了,還能通過不同的字母拼寫出不同的單詞來。慢慢的,你能通過一些基本的語法把這些單詞串成一句話。為了使句子時態正確且沒有語病,你需要用一些介詞,連詞,等等。。看下面這句話 "I" (代詞) "want" (動詞) "to" (介詞) "learn" (動詞) "OOD" (名詞) 通過把幾個單詞擺放妥當一句話就好了,然后用個關鍵詞來說明一下這句話的重點。 妻子: 親愛的,你閑扯這些到底要說明什么呢 丈夫: 我說的這個例子跟面向對象規范很類似,面向對象規范為面向對象編程定義了基本的規范,它是面向對象編程的主要思想。面向對象規范好比基本的英語語法,這些語法教會了你怎么用一個個單詞拼湊出一句句話來,而面向對象規范教你怎么用類,怎么把一些屬性和方法封裝在一個類里,怎么串出類之間的繼承關系。 妻子: 啊哈,我知道了,那么,面向對象適用于哪里呢。 丈夫: 聽我慢慢道來。現在,假設你想寫點有內容有題材的文章。你當然還希望寫點你比較擅長的題材的書,就會簡單造幾個句子是遠遠不夠的,對吧。你需要筆耕不輟寫出一些長篇大論,你還需要學習怎么可以讓讀者很容易就看懂你寫的這些長篇大論。。。 妻子:嗯,有那么點意思。。。繼續吧 丈夫:現在,假如你想寫本關于面向對象設計的書,你需要把這個大的課題拆分成一些小題目。把這些小題目分幾個章節寫,還得寫前言,簡介,說明,舉例,一篇里還有很多段落。你需要設計一整本書,還得練習一些寫作技巧,讓文章讀起來淺顯易懂。這就是綜觀全局。 在軟件開發中,OOD就是用來解決從全局出發考慮問題,在設計軟件的時候,類和代碼可以模塊化,可重復使用,可靈活應用,現在已經有很多前人總結出的類和對象的設計原理了,我們直接拿來用就行了,總之,歷史的車輪已經碾壓出一條清晰的車輪印,我們只要照著走就可以了。 妻子: 哎,懂了點皮毛,還有很多要學呢。 丈夫:不用擔心,你很快就會上手的,讓我們接著來吧。 話題:為什么要進行面向對象設計?作者:有個很重要的問題,既然我們能夠很快的創建幾個類,編寫程序并提交,為什么我們還要關注面向對象設計?這樣不夠么? 妻子:恩,以前我不知道面向對象設計,我也能開發提交項目。有什么關系? 丈夫:好吧,先讓我給你看一個經典的引述:
妻子:你是指軟件開發說明書會被不斷修改? 丈夫:非常正確!軟件開發唯一的真理是“軟件必然修改”。為什么? 要知道,你的軟件解決的是現實世界中的問題,而現實生活不是一成不變的。 可能你的軟件現在運行良好。但它能靈活的支持“變化”嗎?如果不能,那它就不是一個敏捷設計的軟件。 妻子:好,那你就解釋一下什么叫做“敏捷設計的軟件”! 丈夫:“一個敏捷設計的軟件能輕松應對變化,能被擴展和復用。” 而應用“面向對象設計”是做到敏捷設計的關鍵。那么,什么時候你可以說你的程序應用了面向對象設計? 妻子:我也正想問呢。 丈夫:如果代碼符合以下幾點,那么你就在“面向對象設計”:
妻子:然后呢? 丈夫:不只我們。很多人也花了很多時間和精力思考這個問題上,他們嘗試更好的進行“面向對象設計”,并為“面向對象設計”指出幾條基本的原則(你可以用在你的“面向對象設計”中)。他們也確實總結出了一些通用的設計模式(基于基本的原則)。 妻子:你能說出一些嗎? 丈夫:沒問題。現在有許多設計原則,但是最基本的,就是SOLID(縮寫),這五項原則。(感謝鮑勃叔叔,偉大OOD導師)。 S = 單一責任原則 在下面的討論中,我們將詳細了解這些。 話題:單一功能原則作者:讓我們先來看圖,我們應該感謝制作這張圖的人,因為它們真的太有趣了。
單一功能原則圖 它的意思是:“如果你可以在一個設備中實現所有的功能,你卻不能這樣做”。為什么呢?因為從長遠來看它增加了很多的可管理性問題。 從面向對象角度解釋是: "導致類變化的因素永遠不要多于一個。" 或者換行個說法:"一個類有且只有一個職責"。 妻子:可以解釋一下么? 丈夫:當然,這個原則是說,如果有多于一個原因會導致你的類改變(或者它的職責多余一個),你就需要根據其職責把這個類拆分為多個類。 妻子:嗯...這是不是意味著在一個類里不能有多個方法? 丈夫:當然不是。你當然可以在一個類中包含多個方法。問題是,他們都是為了一個目的。那么,為什么拆分很重要的? 那是因為:
妻子:給個例子唄? 丈夫:木有問題啊,瞅瞅下面類的結構。其實,這個例子是 Bob 叔叔那兒來的,得謝謝他。
違反SRP原則的類層次結構 這里,Rectangle 類干了下面兩件事:
而且,有兩個程序使用了 Rectangle 類:
這違反了SRP原則(單一職責原則)! 妻子:腫么回事? 丈夫:你瞅瞅,Rectangle 類干了倆不相干的事。一個方法它計算了面積,另外一個它返回一個表示矩形的 GUI 資源。這問題就有點樂了:
妻子:是很樂。就是說,咱得根據類的職責分開寫唄? 丈夫:必須滴。猜猜怎么干? 妻子:我想想,我尋思這得這么辦: 我瞅著得按職責拆成兩個類:
丈夫:很好。這么個,計算幾何應用使 Rectangle 類,圖形應用使 RectangleUI 類。咱還可以把這倆類分到倆單獨的 DLL 中,然后改的時候就不用管另一個了。 妻子:謝了,我大概明白 SRP 原則了一句話:SPR 就是把東西分到不能再分了,再集中化管理和復用。囔,在方法層面上,咱不也得用 SPR 原則?我是說,咱寫的方法里有很多干不同事兒的代碼,這也不符合 SPR原則吧。 丈夫:你說地不差。方法也得分開,一個方法干一個活。這么著你復用方法,要是改了,也不用改太多。 話題:開閉原則作者:“開閉原則“圖示如下:
圖:開閉原則圖 讓我來解釋一下,設計規則如下: “軟件實體(類,模塊,函數等)應該對擴展開放,對修改關閉。” 這意味著在最基本的層面上,你可以擴展一個類的行為,而無需修改。這就像我能夠穿上衣服,而對我的身體不做任何改變,哈哈。 妻子: 太有意思啦. 你可以通過穿不同的衣服來改變你的外貌, 但是你不必為此改變自己的身體.所以你是對擴展開放的, 對吧? 丈夫: 是的. 在面向對象設計中, 對擴展開放意味著模塊/類的行為可以被擴展,那么當需求變化時我們可以用各種各樣的方法制定功能來滿足需求變更或者新需求 妻子: 除此之外你的身體是對修改關閉的. 我喜歡這個例子. 所以, 對于核心模塊或類的代碼在需要擴展的時候不應該被修改. 你能結合具體例子解釋下嗎? 丈夫: 當然了, 先看下面的例子.這個就不支持 "開放-關閉" 原則:
類的層次結構已經表明了這是違反"開放-關閉"原則的. 你看, 客戶端類和服務端類都是具體的實現類. 因為, 如果某些原因導致服務端實現改變了, 客戶端也需要相應變化. 妻子: 有道理. 如果一個瀏覽器的實現和一個指定的服務器(比如IIS)緊緊的耦合在一起 , 那么如果服務器由于某種原因替換成了另外的 (比如, Apache) 瀏覽器也需要做相應的變化或者被替換掉. 多么恐怖的一件事啊! 丈夫: 非常正確. 因為下面的將是一種好的設計方案:
類的層次關系展示了"開放-關閉"原則 在這個例子中, 添加了一個抽象的Server類, 并且客戶端保持了抽象類的引用, 具體的Server類實現了這個抽象Server類. 所以, 由于某種原因Server的實現類發生了改變, 客戶端不需要做任何改變. 這里的抽象的Server類對修改關閉, 具體的Server實現類對擴展開放. 妻子: 我的理解是, 抽象是關鍵, 對嗎? 丈夫: 是的, 基本上, 你要對系統的核心業務進行抽象, 如果你抽象化做的比較好, 很可能, 在擴展功能的時候它們不必做任何改變 (比如Server就是一個抽象的概念). 你所定義的抽象的實現 (比如, IIS服務器 實現了 Server) 和 抽象的代碼 (Server) 要盡可能的多. 這樣在客戶端代碼中不需要做任何修改就會允許你定義一個新的實現(比如, ApacheServer) . 主題: 里氏替換原則丈夫: "里氏替換原則"聽起來非常的復雜,但是設計思想卻是非常基礎的. 看下面這個有趣的海報
里氏替換原則海報 原則描述了: "子類型必須能夠替換它們的基類." 或者, 換句話說: "使用基類引用的函數必須能夠使用派生類而無須了解派生類." 妻子: 對不起, 這聽起來讓我覺得有點亂. 我認為這個是面向對象編程的基本原則. 這個叫做多態性, 對吧? 為什么面向對象設計原則需要考慮這個問題? 丈夫: 非常好的問題. 這有一些答案: 在基本的面向對象原則中, "繼承" 通常被描述成 "is a" 的關系. 如果一個 "開發者" 是"軟件專業人員", 那么 "開發者" 類 應該 繼承 "軟件開發人員" 類. 這樣的 "Is a" 關系 在類設計階段非常重要, 但是這也很容易讓設計者得意忘形從而以一個糟糕的繼承設計告終. "里氏替換原則" 僅僅是一種確保繼承被正確使用的手段. 妻子:我明白了。真有趣。 丈夫:是的,親愛的,確實如此。讓我們來看看一個例子:
類層次結構圖展示的是一個Liskov替換原則的例子.因為 KingFisher類拓展(繼承)了Bird類,因此繼承了Fly()這個方法,這是非常不錯的. 我們再來看看下面的例子
修正過的Liskov替換原則的類層次結構圖 Ostrich(鴕鳥)是一種鳥(顯然是),并繼承了 Bird 類。但它能飛嗎?不能,這個設計就違反了里氏替換原則。 因此,即使在現實中看上去沒什么問題,在類設計中,Ostrich 都不應該繼承 Bird 類,而應該從 Bird 中分出一個不會飛的類,由 Ostrich 繼承。 妻子:好吧,明白了。我說說為什么里氏替換原則如此重要:
我說的對嗎? 作者:完全正確,你可以設計一個對象并用LSP作為驗證工具來測試該對象是否能夠繼承。 話題:接口隔離原則作者:今天我們講下“接口隔離原則”,看看下面這張海報
接口隔離原則海報 妻子:這是什么意思? 作者:它的意思是這樣的:“用戶不應該被迫依賴他們不使用的接口。” 妻子:解釋一下。 作者:好吧,解釋如下: 假設你想去買一臺電視機并且有兩種類型可以選擇,其中一種有很多開關和按鈕,但是多數對你來說用不到,另一種只有幾個開關和按鈕,并且看來你很熟悉怎么用。如果這兩種電視機提供同樣的功能,你會選擇哪一種? 妻子:當然是第二種了。 作者:嗯,但是為什么呢? 妻子:因為我不需要看起來很麻煩而且對我也不必要的開關和按鈕。 丈夫:正確。同樣的,假如你有一些類,你通過接口暴露了類的功能,這樣外部就能夠知道類中可用的功能,客戶端也可以根據接口來設計。當然那,如果接口太大,或是暴露的方法太多,從外部看也會很混亂。接口包含的方法太多也會降低可復用性, 這種包含無用方法的”胖接口“無疑會增加類的耦合。 這還會引起其他的問題。如果一個類視圖實現接口,它需要實現接口中所有的方法,哪怕一點都用不到。所以,這樣會增加系統復雜度,降低系統可維護性和穩定性。 接口隔離原則確保接口實現自己的職責,且清晰明確,易于理解,具有可復用性。妻子:我明白了,你的意思是接口只應該包括必要的方法而不是所有的。 作者:是的,讓我們看一個例子。 下面的接口是一個“胖接口”,這違反接口隔離原則:
違反接口隔離原則的接口示例 注意,IBird接口定義 Fly()的行為有許多鳥類的行為。現在,如果一只鳥類(比方說,鴕鳥)實現了這個接口,它將會實現不必要的 Fly()的行為(鴕鳥不會飛)。 妻子:是啊。因此,這個接口必須被分割? 作者:是的,“胖接口”應該分隔成兩個不同的接口,IBird 和IFlyingBird,而IFlyingBird繼承于IBird。
接口隔離原則的例子中正確版本的接口 如果有一只不會飛的鳥(比如,駝鳥),只要用IBird接口即可,如果有一保會飛的鳥(比如,翠鳥),只要用IFlyingBird接口即可。 妻子:所以,回過頭來看有很多按鈕開關的電視的例子,制造商應該有電視機的圖紙,開關和按鈕也在這個方案里。若他們想造一臺新款電視機時想要復用這張圖紙,他們必須添加更多的按鈕和開關,否則沒法復用,對么? 丈夫:對。 妻子:若是他們真的想要復用這個方案,他們應該將電視機的圖紙分為更小的部分,才能在以后制造新款電視機的時候復用這些設計方案。 丈夫:你理解了。 話題:依賴倒置原則作者:這是SOLID原則中最后的原則。圖示如下:
依賴倒置原則圖示 它的意思是: “高層次的模塊不應該依賴于低層次的模塊,而是,都應該依賴于抽象。” 作者:我們用一個現實的例子來理解。你的汽車是用很多部件組成,比如發動機,車輪,空調和其他的部件,是吧? 妻子:是啊,當然是這樣。 丈夫:你看,它們并沒有嚴格的構建在一個部件里;就是說,它們都是“插件”,要是引擎或著車輪出了問題,你可以單獨修理它,甚至換一個用。 替換時,你只需要保證沉淪符合汽車的設計(汽車能使用任何1500CC的引擎或任何18寸的車輪)。 當然,你可以在1500CC 的位置上安裝2000 CC的引擎,對某些制造商都一樣(豐田汽車)。 可如果你的汽車部件不是“可拔插”的呢? 妻子:那太可怕了!這樣的話,要是汽車引擎故障,你得整車修理,或者買一輛新車! 丈夫:是的,那么怎么做到"可插拔"呢? 妻子:關鍵是”抽象“,是吧? 丈夫:對。現實世界中,汽車是高層級的模塊/實體,它依賴于底層級的模塊/實體,例如引擎和輪子。 相較于直接依賴于實體的引擎或輪子,汽車應該依賴于抽象的引擎或輪子的規格,這樣只要是符合這個抽象規格的引擎或輪子,都可以裝到車里跑。 來看看下面的圖:
依賴倒置原則的類層次結構 丈夫:注意上面的 Car類,它有兩個屬性,且都是抽象類型(接口)而非實體的。 引擎和車輪是可插拔的,這樣汽車能接受任何實現了聲明接口的對象,且 Car 類無需任何改動。 妻子:所以,如果代碼不遵循依賴倒置,就有下面的風險:
丈夫:親愛的,你說到點子上了! 總結丈夫:除 SOLID 原則外還有很多別的面向對象原則。比如:
妻子:我得學習這些原則嗎? 丈夫:當然了。你可以在網上學習。Google 它,學習它,理解它。有問題就找我。 妻子:我聽說還有些根據設計原則編寫的設計模式。 丈夫:對的。設計模式不過就是針對一些經常出現的場景的一些通用的設計建議。主要的想法還是面向對象原則。你可以認為設計模式是“框架”,OOD 原則是“規范”。 妻子:那么之后我將學習設計模式是吧? 丈夫:是的,親愛的。 妻子:應該會很有意思。 丈夫:必須地! 本文中的所有譯文僅用于學習和交流目的,轉載請務必注明文章譯者、出處、和本文鏈接 該文章在 2013/1/20 0:21:59 編輯過 |
關鍵字查詢
相關文章
正在查詢... |