作者leondemon (狗狗)
看板OOAD
標題Re: [問題] 學習物件導向初學
時間Sun Oct 25 01:03:24 2009
※ 引述《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