看板 OOAD 關於我們 聯絡資訊
※ 引述《SpiritKnight (無心)》之銘言: : 板上的前輩好我也想學習學習物件導向,我是電子系的,卻對程式比 : 較有興趣,但是因為本身不是資訊底子出身,所以對程式了解並沒有 : 很深,都是上上課,學學指令,基本的什麼For,Do,If...等等的這些 : 學到都爛了,但是每當自己想提起手來寫個程式的時候,小程式易寫 : ,但是稍大一點的就像撞了牆一樣,怎麼都感覺怪怪的,可是該會的 : 指令就明明都會,卻不知道問題出在哪,後來一直有人建議我去了解 : 物件導向(雖然我不知道主因是否在此),但是一直常聽到這個詞,後 : 來不論是去上網查還是去書局翻書,發現不只"物件導向"這四個字我 : 認識,書裡的每個字我也都認識,但是組合起來卻似乎沒有辦法那麼 : 熟悉它到底在說什麼,通常不外乎就是一堆理論,想請教板上的高手 : ,有什麼書或是網站,是對物件導向深入淺出,並且有將物件導向與 : 傳統的程式設計方式做相互比較的書,適合對物件導向很沒概念的人 : 看的書嗎? 謝謝各位前輩~ 看到這篇實在是有感而發 小弟開始接觸寫程式也是半年內的事 更何況寫的code沒幾行(最初接觸原因是為了parse某個網站資訊) 最近畢業後 上個月才開始認真的研讀程式語言和物件導向(因為真的有興趣) 我發現「物件導向」四個字真的是個屁 很多人舉「汽車」例子來解釋物件 接著講解實例(instance)和繼承(inheritance) 然後封裝(encapsulation)、抽象化(abstraction)和多型(polymorphism)則是含糊帶過 初學者根本聽不懂 這種說明真的是模糊了Object-Oriented的精神 到最後就是錯誤的使用OO 而不斷的subclass和創造物件來撰寫程式 這些完全沒辦法讓學習者了解OO的真正使用方式 只是為了解釋而解釋 這讓我學習OO的路上繞了一大段路 直到我唸了歐萊禮出版的「深入淺出物件導向分析與設計」後才恍然大悟 (雖然這本書沒有講Object到底是什麼,接下來的內容完全是個人體悟,請多指教) 真的很希望能將Object-Oriented programming翻譯成「以『目的』為取向的撰碼」 我認為Object-Oriented: 是為了管理操作而將程序式撰碼(procedural programming)分成許多目的(object)撰碼 而不是為了物件(object)存在而去產生屬性(property)方法(method) 簡單的說 OO是為了「管理」程式而將程式碼和資料依照目的(Object)分類(Class)管理 而不是為了要「模擬」某種類別(Class)物件(Object)所應該具備的特質(屬性及方法) 因此程式碼之屬性(變數)及方法(功能)是為了同一目的而存在同一Object中 方便管理 而任何需要「管理」的 一定是個大東西 所以越大型程式用正確的物件導向越容易維護 OO最主要的精髓並不是在於繼承 而是在於封裝 封裝(encapsulation)就是將程式碼分開到特定的class 而這些程式碼具有相同的目的 透過封裝 將程式碼依照不同的目的分門別類(class) 以方便使用與管理 透過封裝 將不同的資料得以分開操作(instance) 以方便使用與管理 透過封裝 可以將固定的程式碼和經常變動的程式碼分開 以方便管理和使用 繼承(inheritance)是為了abstration而存在 **abstration大多翻譯為「抽象化」但是我會重新翻譯為「提取」 **由於我對於abstration這個字的意義有不同解讀 因此本文關於這字的解釋會有所不同 「提取」是將重複的程式碼抽出來 封裝一個class裡面 簡單的說是要避免程式碼的重複 再藉由繼承來分配到必須擁有這些特性的class當中 換句話說使用繼承的目的是為了提取 將不同目的中 擁有共同的目的之特性抽取出來管理 而繼承不是為了創造新的類別或物件 因此 沒有需要abstration的理由 就沒有需要inheritance的理由 多型(Polymorphism)是傳遞多種類別的接口 以達許多片段程式碼的固定 **多型的解釋需要code或圖來幫助理解 但是這裡只做文字解釋(原諒我偷懶) 在繼承當中 有個很重要的功能 就是覆寫(overwrite) 也就是可以修改父類別的method 當你的程式碼有某method都具有同源(homogenous,即本質相同)但有些許差異或不同行為 **用生物學眼光來解釋 就是同源基因 但基因序列編碼不同 =>希望能幫助生物人理解 Orz 覆寫原本的目的是為了可以在繼承大部分的父類別的程式碼 又可保有修改些許功能的彈性 但是只為了覆寫一些方法 卻沒有新增新的方法 而去subclass大型Object是個很蠢的作法 > 舉例來說 同樣是「飛(fly)」的方法 但是「動物類別」飛行的方法有很多種 > 但是無論你之後創造了什麼動物(鳥、蟑螂、飛鼠等翅膀或飛行模式並不同) > 你都希望在你創造的「動物類別」中有一個「飛(fly)」的方法可以呼叫 > 初學者很容易就針對這個動物類別subclass出鳥、蟑螂等多個子類別 > 然後直接覆寫「飛」的功能 (如果設計的動物不會飛 則將它覆寫為沒有功能) > 這種就是為了「物件」的存在而去「繼承」來寫OO的方式 > 只為了「覆寫」卻沒有新增任何功能而將類別進行subclass不是一個好的管理程式方式 > 較好的作法是將「飛行」視為一種「目的」而將其封裝出來成為一個類別 > 該類別中只有一個「飛」的方法 > 然後針對「飛行」類別進行subclass(鳥類飛行、昆蟲飛行、飛鼠飛行、蝙蝠飛行等) > 覆寫subclass中的「飛」的方法 > 最後在「動物」類別中 建立一個屬性為「飛行」型態 > 然後只要在原先「動物」的類別中的「飛」去呼叫「飛行」類別中的「飛」 > 這樣就可以不必在更改任何「動物」的code或是subclass > 然後在創造實例(instance)後設定飛行的類別是何種「子類別」 **以上舉例不懂沒關係 因為我自己都快看不懂了 Orz 重點是 當你subclass的東西越龐大 就越容易出錯或難以維護 而利用多型 就是將同源(同樣名稱)但不同功(功能)的方法 抽離並封裝出來 利用inheritance/abstraction這樣的關係 來重新覆寫子類別的方法 這樣只要在原先類別固定呼叫抽離封裝出之類別的方法 實作則在該子類別當中 而只要在創造實例時決定呼叫哪種子類別(設為變數)即可 這樣就可以固定原先類別的程式碼 (以後要新增飛行方法都可以不必更改到動物類別) 也可避免對大型類別進行過度的subclass了 至於我認為什麼是Object? 其實前面說的很清楚了 Object就是某種「目的」 所有屬於這個目的的方法、變數當應該分類(classify)到同一個類別(class)中 我將這些目的(Object)可以分為下面幾種: *資料分類目的: 此Object是為了存放不同實例(instance)的資料 而將這些相關變數放在一起 而其應該包含的方法應該都是與存取(accessor)或操作這些instance variables有關 這樣可以在實作時將不同實例的的資料分開來管理和操作 其他無關的方法或屬性則應該封裝出來 *方法分類目的: 主要是操作相同目的之method 分類在同一類別中 而其所帶有的變數(或稱屬性)則是為了這些方法所需紀錄保存的參數 此Object的存在 最主要是提供不同的Object可以呼叫同樣的功能 *架構固定目的: 簡單的說 就是前兩者Object中需要實行abstration出來的Object 存在的理由只有兩種 1.將相同的方法和屬性提取出來 2.為了使用多型 其目的都是為了避免重複程式碼以減少需要的管理 將「固定」之物抽離出來固定 讓變化的部份在繼承中實作 (另:封裝亦是將「變化」之物抽離出來管理) 因此這些「固定」出來的Object 即會變成一種較大的架構/骨架(因為幾乎不會變動) 也就是因為主要利用abstraction/inheritance的「結果」來決定固定的架構 由於使用繼承比使用封裝來處理「變化」部份的程式碼要來得簡單 造成大部分的人在學習或使用OOP的時候 認為subclass才是OOD的精髓 卻忘了OO一切都是用封裝來處理「不同本質的變化之物」 這樣有點本末倒置 而繼承只是用來處理「相同本質的變化之物」也就是子類別和父類別本質上是相同的 (以前面提到的例子來說 「動物」和「飛行」在本質意義上是不同的 也就是在撰碼時要修改的原因和存在目的並非一致 所以會封裝出來再利用多型特性結合) 藉由委派(delegation)、聚集(aggregation)、合成(composition)等協同操作方式 是將不同的目的程式碼(objects)整合成一個真正完整功能的物件(object) 所以良好的OOD 1.應該是要把不同目的之程式碼分開 一種Object應該只有一種存在的目的 2.不會有任何重複的程式碼 任何相同目的重複程式碼都必須抽離出來 若是同一物件多個方法會用到相同程式碼 則抽離出來成為同物件下獨立的方法 若是多個物件會用到相同程式碼 則抽離出來成為獨立一個的物件下的方法 3.將應該十分固定及時常變更的程式碼分開 即使目的相同 4.當一個理由需要新增或改寫程式碼時 需要維護或改變的Object應該是非常少的 而不是分散在四處都需要改變 減少程式發生錯誤的可能 才不會牽一髮動全身 需要改變的地方越少 越容易維護和除錯 5.適當的將程式碼分配到不同Object可以快速了解程式的操作 能幫助了解整個程式架構 換句話說汽車(car)應該是由多個Objects協同組成 而不該把car設計成一個單獨的Object(除非程式很小而且你又很懶得拆開來) **如何拆解請參考歐萊禮出版的「深入淺出物件導向分析與設計」p393-395 -- 以上是我對Object-Oriented的淺見 請多多指教 :) -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 163.13.42.121 ※ 編輯: leondemon 來自: 163.13.42.121 (10/25 01:48)
andrew43 :受用, 非常感謝. 10/25 01:43
※ 編輯: leondemon 來自: 163.13.42.121 (10/25 03:03)
idleidle :先推後看 10/25 11:18
adrianshum :最後一段只令我覺得你一知半解. car 是一個 class 10/25 17:57
adrianshum :還是讓由多個obj 組合, 誠如你所說, 該看你的 car 10/25 17:57
adrianshum :目的是什麼. 這樣沒頭沒尾的說 "car 是一個obj 是錯" 10/25 17:58
adrianshum :實在不能苟同 10/25 17:58
leondemon :我沒說"car 是一個obj 是錯"吧 =.= 10/26 11:44
leondemon :我是說car應該要由多個Obj組成 除非沒有理由要分開 10/26 11:44
leondemon :舉Car的例子 是因為它常會被視為"物件" 而對它撰碼 10/26 11:50
leondemon :如果用"物件"的思維 很容易就把所有程式碼往它裡面塞 10/26 11:51
leondemon :但如果用"目的"去思維 就會重新思考程式碼的封裝方式 10/26 11:53
leondemon :Object翻譯成"目的"或"主題"會比"物件"提供思考幫助 10/26 11:55
qrtt1 :如果不用他原先該有的想法去思考, 10/26 13:07
qrtt1 :要同時扭曲很多解釋的方式, 這樣太過間接了 10/26 13:07
TeaEEE :雖然打了很多字 不過不是很同意你很多的看法 10/26 17:56
kanandg1 :要如何解釋實作介面(或繼承虛擬類別)呢? 10/27 16:48
kanandg1 :他們沒有程式碼,不能算code 的reuse吧? 10/27 16:49
leondemon :很歡迎大家指教與討論 因為我也是還在學習! 10/27 19:15
H45 :回文回文回文回文回文回文回文回文回文回文回文回文 10/27 20:19
Davidjcan :我也是剛學習,我覺得原po寫的不錯 10/27 21:09
adxis :有Aspect Oriented的味道 10/28 20:37
adrianshum :"car...協同組成", "不該...單獨的 Object" 10/29 11:39
adrianshum :所以我才說, 視乎你設計 car 的目的是什麼, 沒有應 10/29 11:40
adrianshum :不應該協同組成. 另外, 我覺得把 object 想成目的才 10/29 11:41
adrianshum :有問題. 建議你去看看 AOP, 那才真的是以目的出發. 10/29 11:41
adrianshum :倒是我贊同, 定義 class 的時候, 該把每種 "目的" 10/29 11:48
adrianshum :細分, 才能避免你提到那種 "通通都包" 的 class 10/29 11:49
aecho :看到這麼長的文章還蠻感動的~~先推一下 11/04 22:19
twntwn :不太同意, 物件導向是透過對真實世界的塑模來填補 12/16 05:52
twntwn :從問題領域到解決方案領域間的gap.的一種方法論. 12/16 05:54
twntwn :ood是物件導向流程中的一項活動,目的是導出解決方案, 12/16 05:56
twntwn :有關抽像封裝等設計物件的技術,OCP,LSP,DIP,SRP.ISP 12/16 05:58
twntwn :可以去了解一下,因該會讓你修正一下目前的想法。 12/16 06:01
leondemon :謝謝大家的指教 我也只是OO初學者 我會努力上進的! 01/11 21:11
larrywhy :好文推 仔細琢磨ing 04/09 13:55