Re: [閒聊] OOP小評

看板Soft_Job作者時間10年前 (2015/03/05 15:12), 10年前編輯推噓6(6016)
留言22則, 10人參與, 最新討論串26/43 (看更多)
這一系列下來,看到不少對 OOP 、重構有誤解的地方 一、要理解 OOP ,需要先知道「OOP 想解決的問題是什麼?」 先看整個程式設計的發展 一開始是機械語言、組合語言 再來是程序式語言,所以衍生了結構化編程、流程圖的出現 之後是物件導向,衍生了 UML 如果拿不適合的標準去評論 OOP, 某種程度上跟問「你為什麼要用 C 寫,組合語言不是比較快」沒什麼不同 OOP 想解決的問題是什麼? 當「軟體規模」很大時,需求變動造成軟體很大的修改,甚至引起很多的問題。 在這沒有認知到這個問題時,一些批評 OOP 的論點就很有問題 A.關於「疊床架屋」 舉例,常常會看到類似的程式: void A() { doSomething1(); B(); doSomething2(); } void B() { C(); } void C() { doForC(); } 在這個例子中, A() -> B() -> C() -> doForC(); 但 B() 根本沒做什麼事,只是直接再呼叫 C() 而言 這根本是「疊床架屋」,為何不直接 A() -> C()? 甚至 A() -> doForC()? 這樣的說法未必是錯的,但是 OOP 有另外的考量 舉個例子,在 OOP 中可以「繼承」 C() void C() { doAnotherForC(); } 這樣一改,C() 的行為變了,但不影響 A() 的邏輯。 此外,也可以改寫 B() (改寫方法有很多,同樣繼承是一種方法) void B() { if(case1) { C(); } else { D() } } 如果不用 OOP() ,那寫法可能變成: void A() { doSomething1(); if(case1) { doForC(); } else if(case2) { D(); } else if() { } doSomething2(); } 整個 A() 的邏輯就變大、變複雜 甚至,當 case1, case2 的邏輯改變時(譬如加入新的資料型態),A() 都要跟著改。 說到這,就想到一個有趣的故事: 問:現在有水壺、瓦斯爐、水龍頭,水壺內沒有水,現在要燒一壺熱水,要怎麼做? 答: 1.打開水龍頭,將水壺裝滿水 2.將水壺放上瓦斯爐 3.開啟瓦斯爐 再問:如果有水壺、瓦斯爐、水龍頭,水壺內裝滿水,要燒一壺熱水,那要怎麼做? 一般人會回答: 1.將水壺放上瓦斯爐 2.開啟瓦斯爐 數學家會回答: 1.將水壺的水倒掉 2.回到上一題的解法 有注意到嗎?數學家的回答是充分用已知的解答來回答新的問題 儘管當你深入去研究時發現:要先倒掉水,再裝滿水,不就是白費工? 於是,你要說:OOP 是疊床架屋? 是的,它是,但是它有它要解決的問題(軟體規模大到一定程度), 所以疊床架屋,但是最主要的問題不是「疊床架屋」,而是「軟體規模大」 B. 關於將資料和程序放在一起 我個人覺得更精確的說法是:將「相關的」資料和程序放在一起 想像我們現在在蓋一間房子,有兩種可能: I. 每間房有各自的家電用品,開關都在各自的房間 II. 每間房有各自的家電用品,開關都集中在某一間房 那麼,我們會蓋哪一種? OOP 是認為:對於資料,只有跟他有關的程序才能接觸他。 不太確定你所謂的「資料和程序不要放在一起」會是什麼情形? 就像是 C ,也會把 public A() { if(x > 5) { // } else if(x > 2) { } else { } } C. OOP 沒變法定量或定性嗎?未必 我自己的定義就是「能省下多少功」 二. 關於「重構」 有版友提到:重構感覺就是在鬧革命 這只能說,對「重構」是完全的誤解 首先,重構的粒度有大有小 小到改變數名稱,大到修改整個繼承體系乃至於重寫,都是重構 (甚至,我個人都覺得,排版、增加空行、修改註解也可以算是重構了) 重點在於:要在什麼時間點,選擇怎麼樣的粒度 如果在要出貨前,來個如修改整個繼承體系一樣的重構,那基本是弄錯重構的精神 我個人對「重構」的定義是:「有系統地」「改善」「既有軟體」 因此,「有系統地」是一個很大的重點。 題外話: 不要以為重構改只有改註解,就不會有問題 有時候註解也是會產生 bug,譬如下面這個例子,用 gcc 會出問題 http://ppt.cc/WA0v (預期會是 437 ,有 bug 時是 400) ================== comment_matters.cpp ================== #include <stdio.h> int compute(int n) { // 加法 n+=3; // 乘法 n*=4; // 自己乘自己乘自己 n*=n; // 最後再加上37,希望能成功 n+=37; printf("The result: %d\n", n); } int main() { compute(2); } ================== comment_matters.cpp ================== -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 140.112.30.46 ※ 文章網址: https://www.ptt.cc/bbs/Soft_Job/M.1425539540.A.BD7.html

03/05 15:31, , 1F
同感 OOP我也覺得主要是降低複雜度
03/05 15:31, 1F

03/05 15:32, , 2F
呃 應該是"降低我自己在寫code時的複雜度"更好XD
03/05 15:32, 2F

03/05 15:33, , 3F
話說回來這一系列文為什麼要在這出沒而不是去CPP板 XDD
03/05 15:33, 3F

03/05 16:29, , 4F
推這篇
03/05 16:29, 4F

03/05 17:01, , 5F
因為OOP!=CPP
03/05 17:01, 5F

03/05 17:10, , 6F
請先定義"軟體規模大到一定程度" 這明顯是一個程度量詞
03/05 17:10, 6F
需要定義嗎? 當你遇到一堆有的沒有維護的問題,你就會知道什麼是一定程度了

03/05 17:40, , 7F
請問:怎麼弄出400?
03/05 17:40, 7F
你可以把上面的連結(http://ppt.cc/WA0v)的檔案抓下來 用 gcc 或 Dev-C 試試看 不妨猜猜為什麼會如此 :)

03/05 17:41, , 8F
關於B你講的有道理,在某些各自封閉的體系下
03/05 17:41, 8F

03/05 17:41, , 9F
不過我要下班了,我的觀點下次再說了
03/05 17:41, 9F

03/05 17:43, , 10F
一句話:Class不是汎用型,很多狀況(開放體系)不適用
03/05 17:43, 10F
你這句話是不明不白,什麼叫開放體系不適用?開放體系是指 事實上,把相關的資料和程序放在一起,這很常見 假設在用 C 寫大數。 通常也會寫成類似: struct BigNum { } void addBigNum(BigNum&, BigNum&); void multiplyBigNum(BigNum&, BigNum&); 然後將這些放在 big_num.h/big_num.cpp 這就是把相關的資料和程序 只是在 OOP 有更簡單的作法,就叫 class ※ 編輯: oaz (140.112.30.46), 03/05/2015 17:53:15

03/05 18:00, , 11F
sorry,程度量詞無法量化而是看程度的話, 哪很難討論呢.
03/05 18:00, 11F

03/05 18:06, , 12F
是因為中文的關係吧?本來用預覽忘記這可以直接抓
03/05 18:06, 12F

03/05 18:06, , 13F
XD 所以從BBS複製的話是正常的0.0
03/05 18:06, 13F

03/05 18:11, , 14F
從BBS複製的話,也未必會對,在某些情況下還是會出問題
03/05 18:11, 14F

03/05 18:14, , 15F
是喔0.0 程式內不要有中文就可避免了吧?
03/05 18:14, 15F

03/05 19:08, , 16F
許功蓋? :D
03/05 19:08, 16F

03/05 19:09, , 17F

03/05 19:10, , 18F
應該是許功蓋...
03/05 19:10, 18F

03/05 19:10, , 19F
早年Dev-C++帶的MinGW很有問題...
03/05 19:10, 19F

03/05 19:14, , 20F
是的,是許功蓋的問題。所以不要用 big-5 就可以了
03/05 19:14, 20F

03/05 19:15, , 21F
Dev-C++ 不確定,但 gcc 還是一樣的問題。
03/05 19:15, 21F

03/05 20:35, , 22F
不知道為什麼,感覺舉的例子不是很好
03/05 20:35, 22F
文章代碼(AID): #1K-07KlN (Soft_Job)
討論串 (同標題文章)
以下文章回應了本文
閒聊
-2
14
完整討論串 (本文為第 26 之 43 篇):
閒聊
-14
103
閒聊
4
24
閒聊
-11
76
閒聊
11
45
文章代碼(AID): #1K-07KlN (Soft_Job)