看板 Soft_Job 關於我們 聯絡資訊
首先,要先瞭解物件導向的目的 我個人個看法,物件導向已經算軟體工程了 而軟體工程的目的就是管理更大規模的程式 但是在這幾篇文章中, Luca Cardelli 和 Axel-Tobias Schreiner 的著眼點主要都是在效能 這就好像你用組合語言來評論高階語言及編譯器的效能一樣 又好像你蓋了一間小木屋,然後批評蓋高樓大廈的人怎麼這麼慢 效能不是不重要,而是軟體工程有軟體工程的玩法 而軟體工程的玩法,最主要就是八十/二十法則 簡單來說,就是先找到效能的瓶頸,然後針對這部份做最佳化 甚至,可以將瓶頸的部分改用低階語言寫 此外基於物件導向,可以有更多、更深入的課題:單元測試、重構。 當然,非物件導向語言,也可以有單元測試、重構的觀念, 但我個人認為,這主要是程式設計師非物件導向語言時, 已在某種程度上引入物件導向的觀念,如:封裝 就像也可以用 C 寫得很接近 OO ,這會在之後說明 另外,不得不提到 Joe Armstrong 看起來是不懂 "物件導向" 的觀念 他的第一點是,資料結構和函數不應該綁在一起 (Data structure and functions should not be bound together) 很明顯,他是在用敘述式的語言的角度在看 "物件導向" ,因此他看不懂為什麼 問題在於,很多時候,資料和函數是有關聯的 就像,你不能叫一張床 "吃" 一個枕頭,或是對一台車 "開平方" "吃" 這個動作只能跟生物有關,而 "開平方" 只能跟數 當然,對敘述式的語言,就是以 "型態檢查" 來避免這類的情形。 但是,當你有一萬個變數和一千個函式,要怎麼做管理這麼多的變數和函式? 物件導向就是提供一個觀點:將有相關聯資料函式放一起,稱作類別 這就是物件導向的一大特性:封裝 只要掌握封裝的精神,就算用非物件導向語言,也可以寫出物件導向的風格, 以 C 來說: Struct BigNum { int numDigits; char* digits; } void BigNum_Init(BigNum*) { } BigNum* BigNum_Add(BigNum*, BigNum*) { } BigNum* BigNum_Minus(BigNum*, BigNum*) { } 其實跟以下是差不多的 class BigNum { public BigNum(); BigNum Add(BigNum); BigNum Minus(BigNum); } 甚至,有些事,就是資料跟函式綁在一起 假設,有一個自動販賣機的程式,使用者按一個鈕,就會掉出飲品 如果以 "資料跟函式分開" 來看, struct 飲品 { int 飲品編號 int 價格 } 大致會有一個資料 按鈕編號 飲品 1 飲品_8 2 飲品_7 3 飲品_2 4 飲品_5 會有一個函式: def 按(int 按鈕編號) { 根據 按鈕編號 查出 飲品編號 和 價格 if 投入金額 >= 價格: 將符合 飲品編號 的飲品掉落 } 這樣是沒問題的 但如果是 "排版軟體" 如 Word 呢? 按鈕功能有複製、貼上、改變顏色、改變大小 以 "資料跟函式分開" 來看 class 命令{ virtual void 做() = 0; } class 按鈕{ 命令 m_命令; void 按下() { m_命令.做(); } } class 複製 : 命令 { void 做() { 將所選文字複製 } } class 改變顏色 : 命令 { int color; void 做() { 改成 color 這種顏色 } } 對整個軟體的宏觀而言,只要知道 按鈕.按下() 就好, 真正做什麼事(函式)、帶什麼參數(資料),都已經被包裝起來了 像這樣的程式,又要如何 "資料跟函式分開" ? 不過,要在此聲明 "資料跟函式分開" 是好的軟工精神 但這跟 "將有關的資料與函式放在一起" 是不違背的 應該說,因為在主幹的部分,將資料與函式分開,所以才在枝節的部分,將有關的資料與函式放在一起 第二點,任何東西都必須是一個物件 (Everything has to be an object) 問題在於,這有什麼不好? 作者只提到了他可定義新的型態,到處都可以用 如:在 Erlang 中,他可以 -deftype day() = 1..31. -deftype month() = 1..12. -deftype year() = int(). -deftype hour() = 1..24. -deftype minute() = 1..60. -deftype second() = 1..60. 但是,這種只是語言特性,而非物件導向的限制。 非物件導向的語言也不一定有這個功能,如 C 第三點,在物件導向語言中,到處都散佈著型別定義 (In an OOPL data type definitions are spread out all over the place) 基本上,我自不懂作者想表達什麼 一開始提到,在 C 和 Erlang 中,他可以在單一檔案中找到他自訂的資料型別, 但在物件導向中,他做不到 但是,問題在於:為什麼一定要在 "單一" 檔案中找到自訂的資料型別? 在 Java 中,有套件(package)的觀念,在 C++ 中,有 namespace 的觀念 接著,他提到了他的例子,他需要一個普遍存在的資料型別,如鏈結串列、雜湊,或時間、檔名。 在 OOPL 中,他則定義一個資料結構,然後所有要使用這種資料結構的物件,都要繼承它 就以 Java 來說,基本上已經有 java.util.* ,C++ 也有所謂的 STL 並沒有所謂要使用鏈結串列的人,都要另外寫一個物件去繼承。 我個人的感覺是,作者無法以 OOP 的角度,將這種資料型別做抽象化。 以及作者把應該要用 "委託" 的部分,用 "繼承" 去實作,因此覺得 OOP 很蠢 第四點,物件有私有(private)狀態(Objects have private state) 事實上,我不是很懂他的觀點,他只是單純說 “將狀態隱藏是最糟糕的策略 但他也沒有說為什麼這是糟糕的 多型的話,可以用 "函式指標" 來達到。唯一的不同點在於 物件導向的語言,很多功能是 "編譯器" "自動" 幫你完成 我唯一想到非物件導向語言做不到或不容易做到的是 "繼承" ※ 引述《mahoihei (Alvar)》之銘言: : 早上看到一篇對個人來說很衝擊性的文章 : http://goo.gl/z4Fa3 : 為什麼說是很衝擊性,因為我自己的編程基礎由oop開始的 : 而在oop design更是我在這個領域最喜愛的地方 : 想問大家對這篇文章有什麼見解?? -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 140.112.30.45
Lordaeron:stackeoverflow有繼承的處理方式. 而你講的, 80/20 02/26 17:53
Lordaeron:在某些場合, 在DESIGN TIME 沒想到, 到發現時要大改 02/26 17:54
Lordaeron:哪就像CACHE哪個例子哪樣了. 02/26 17:54
Lordaeron:正如QUAKE ENGINE一直都C 寫, 寫久了, 有經驗了, 最近 02/26 17:55
Lordaeron:換成C++ 了. 80/20? 發生在--->你有寫過. 02/26 17:56
remmurds:推這篇 02/26 18:34
rodion:推~ 02/26 22:08
dryman:我想別人提出效能議題,是說在某些瓶頸也有OO力所不能及 02/26 22:36
dryman:的地方吧 02/26 22:36
Lordaeron:因為本篇的作者是典型的OOPer. 所以才有80/20的妙論 02/26 23:15
Lordaeron:正如他不知,當年其實JAVA 罵C++ 不夠O, 多一個什麼 02/26 23:17
Lordaeron:generic 的, 一點都不O, 結果JAVA 的O不夠快, 還是死死 02/26 23:17
Lordaeron:的回去generic 上. 02/26 23:17
viper9709:推這篇~ 02/26 23:45
snaketsai:推這篇 02/28 16:26
remmurds:C++被罵不夠OO不是因為Generic吧? 是因為C++搞多重繼承 02/28 16:40
remmurds:搞得繼承類別和實作interface之間分不清界線混在一起 02/28 16:43
snaketsai:還有Function不是Object也是個痛點,High level func. 03/01 00:15
snaketsai:programming只能靠函數指標權充、雖然C++11加了lambda 03/01 00:16
snaketsai:但是離「純種」OO語言還是有一段距離在 @@ 03/01 00:18
s3748679:C++怎麼樣也不會變成樓上所說的純種吧.. 03/03 23:59