※ 引述《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