看板 C_and_CPP 關於我們 聯絡資訊
※ 引述《Sirctal (母豬母豬 夜裡哭哭)》之銘言: : 不好意思,小弟我衍伸出一些疑問。用Template實作Strategy Pattern是不是有點失 : 去他最大的好處?? 因為畢竟這個模式最大的賣點就是run time下可以一個介面變換 : 不同的演算法。那麼用template的用途是? 我為什麼不直接去call那個演算法的物件 : 就好了?? 還要透過你template再一層。 我看Gof的書上說Strategy Pattern還有另外一點 : ,就是你如果演算法有用到不想給人家知道的資料結構或是機密。那可以在用他包一層 : 。可是感覺不出來這樣就可以不讓人家看到耶... : 以上問題懇請回答 : 謝謝 時間不早了,簡單說一下,因為好像真的很多人讀完書以後亂用然後卡住。 GoF 的範例大都是立足在動態多型的世界,用 template 實作是立足在靜態多型的世界。 這兩個世界的面向有決定性的不同之處。 動態多型:通常你的 user 就是 end-user,不會寫程式,只是使用你的程式。 靜態多型:通常用在 library 設計,你的 user 是 programmer,他們會寫程式。 換句話說,靈活性是不是要作用於 runtime,取決於你程式和 user 的類型。 回歸到 design 的初衷,就是在遇到需求改變時,能夠靈巧地去應對。 你不需要修改太多 code,甚至大部分的時候只要新增 code,不會動到已測試過的部分。 我一向反對 programmer 在學 OOAD 之前就先學 OO design pattern,就是怕有人搞錯。 沒有 design 的基礎,只是把 design pattern 的好處拿來當 coding 工具,容易錯亂。 runtime 可抽換的特性是來自於動態多型,而動態多型是大多數 OOPL 都有的特性。 這個特性並非來自 design pattern 本身,design pattern 的用意也非如此。 目前市面上的書看起來都沒在釐清這些觀念,所以常常讓讀到的人很混亂。 一旦你要探討靜態多型版的 design pattern,那你就要以 library 設計者的角度出發。 你的 user 就是使用你 library 的 programmer,需求來自於各個不同性質的 project。 在一個特定的 project 裡,通常也只會用到一兩種組合,而且不需要 runtime 切換。 你的世界觀要從只是完成眼前的 project,擴大成幫助不同人完成各種未知的 project。 我知道要把視角切過來會不太直覺,因為國內學術界跟業界都不太有機會實作 library。 如果讀過 Modern C++ Design 這本書,policy-based design 是很早就介紹的概念。 在後續的章節裡,作者幾乎每一章就會做出一個 library,並在章末彙整用法。 在這裡可以清楚看到作者提供各種 policy 給 programmer 去選擇,這些都是好例子。 所以這個抽換的概念,在靜態多型的世界裡是這樣去理解的,不知道會不會很難懂? 因為我實在是很懶得想去找範例或想範例,這樣發一篇就天亮了 XD ****** 洗澡的時候想了一下,還是找個靜態多型的 library 來說明好了。 希望能很快解釋完。 拿 Boost.Multiprecision 這個 library 當例子,眾所皆知它可用在大數運算上。 http://www.boost.org/doc/libs/1_61_0/libs/multiprecision/doc/html/index.html 不知道怎麼用的話,在 Introduction 那邊有基本用法,我這只說明必要的部分。 簡單說,boost::multiprecision::number<> 可以宣告一個大數型別, 而在角括號裡可以選用後端的不同實作。 number<cpp_int_backend<>> 就是選擇 cpp_int 這個後端。 number<gmp_int> 就是選擇 gmp_int 這個後端。 整數類型可選擇的各種後端,在這裡有列出: http://goo.gl/p3t6MZ 浮點數類型可選擇的各種後端,在這裡有列出: http://goo.gl/8lZovH 我們拿整數類型可選的後端比較表來看,可以很快歸納出選擇者會有的考量,譬如: 1. 目前的這個專案是否與 GPL 或 LGPL 相容? 2. 目前這個專案是否除了和 boost libs 相依外,還要相依其它 libs? 3. 哪個實作可以有最快的執行速度? 在一個專案裡,基於這些考量的不同組合而得到的結論,通常也只會敲定一個。 譬如你覺得用什麼 license 都沒差,只是想要執行得快,那就會選 gmp_int。 如果你的專案跟 GPL 和 LGPL 都不相容,而 Boost 的 licesne 跟你專案沒有衝突, 又不希望專案多相依一個 libtommath,那你可能會選擇 cpp_int。 一旦你決定在這專案要用 cpp_int 的後端,你就不會想在 runtime 切成 gmp_int。 你的 user 並不特別在意你後端是什麼,你想讓他們選,還得先寫一堆文件教育他們。 如果只是開發一個普通又大眾化的應用程式,你並不需要在 runtime 切換後端。 因此,Boost.Multiprecision 在這部分被設計為使用靜態多型,而不是動態多型。 如果你的視角是在開發一個需要用到大數運算的程式,也熟 GMP,可能就直接用 GMP 了。 Boost.Multiprecision 的存在與否,為何如此設計,都跟你沒有什麼關係。 你也不可能因為要開發一個應用程式,就先開發一套像 Boost.Multiprecision 的 lib。 實際上這也是國內絕大多數 programmer 一生當中的視角,因為產業型態就是如此。 在學校為了交一個作業去開發出 Boost.Multiprecision,只會變成笑柄。 同學會說你學技術學到走火入魔,做一件簡單的事繞這麼大圈,別人早你十天就寫完了。 在公司,你只會被主管當成拖慢進度的麻煩人物,還可能被大家當成神經病。 我說這些並不是建議你應該繞遠路,只是在說沒什麼契機對多數人而言是相當正常的。 如果你真的因為這些理由去做一套,那我也會笑你,覺得你搞不清楚狀況。 那你可能會問,如果都沒使用靜態多型設計程式的契機,是不是就不必使用它了。 答案是肯定的。因為你沒有需求,沒有動用這項機制的理由,你就不該一直想著要用。 知道這項機制存在,並且概略知道原理,其實就很夠了。 事實上,在契機出現之前,悶是硬想要怎麼去運用一個機制,是很危險的心態。 這有點像小孩子拿到打火機,覺得打火機能點火很好玩,就四處找地方點火一樣。 你不知道,又勇於提出來問,找知道的人回答你,我覺得這點已經贏過很多人了。 實際上不管是靜態多型還是 template metaprogramming,在需求真正到來前, 事先學會或略懂的人常常只是為用而用,然後都用錯地方,反而讓程式很難維護。 比較正常的方向,應該是先想著要去解決某個問題,然後才去選擇適當的工具。 要反方向走的話,很容易變成拿打火機四處亂點火的小孩,成為令人頭痛的人物。 這十幾年間一直有一些玩火死小孩讓我很頭痛,所以有感而發,講了不少廢話 XD 可能和學校軟體設計的教育完全失敗,相關技術跟知識絕大多數人是自學這點有關。 然後學習資源斷層也是不小,基礎書籍之後,就是直接往一些很進階的主題跳。 design pattern 變成為了優越感而學,為了優越感而用,或者只是為了跟流行。 TMP 很突兀地出現在程式裡,問為什麼,結果說只是因為覺得這樣寫比較帥。 另一種答案是花了很多時間和心力去搞懂,懂了以後都沒地方用,就用用看。 還有就是因為會某種複雜設計的人很少,以後其他人很難接,這樣他的重要性會提升。 聽到這些奇奇怪怪的理由,我也只能跪了... 我這篇也留下了一個疑問: 「那到底什麼情境下會想去設計像是 Boost.Multiprecision 這樣的一個 library?」 這個問題我不打算幫你解答,你也不要在正式場合裡為了做這種東西而做。 因為在你第一次親身處於有此需求的情境之前,別人講什麼其實都很難真正去體會。 如果一生都沒有機會處在那種情境下,並不表示你就低人一等,只是選擇的路不同。 當然如果有一天你有機會處在那個情境裡,也歡迎你回板上跟大家分享你的心得。 一旦你有機會從事這類 library 的設計,不管是休閒還是工作,都會有很多體悟。 -- Ling-hua Tseng ([email protected]) Andes Technology Corporation Compiler Group of RD/Software Division Homepage: http://blog.tinlans.org -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 220.132.55.117 ※ 文章網址: https://www.ptt.cc/bbs/C_and_CPP/M.1473355423.A.378.html ※ 編輯: tinlans (220.132.55.117), 09/09/2016 01:56:14
CoNsTaR: 推 又釐清觀念了 09/09 02:29
johnny94: 講得真好 09/09 03:32
※ 編輯: tinlans (220.132.55.117), 09/09/2016 04:58:49
lovejomi: 請教一下 大數那段, 為什麼會扯到license呢,是因為我 09/09 06:12
lovejomi: 具現化不同backend所以專案 只會感染到那部分的程式碼以 09/09 06:12
lovejomi: 及license規範嗎 09/09 06:12
boost 本身並不包含 GMP 本體,如果要選用 gmp_int,你得另外安裝 GMP library。 一般 Linux 系統要安裝其實沒什麼困難,套件系統的指令打一行就有了。 問題在於你最終的軟體要發佈時,你軟體是不是會把 GMP 打包在一起發佈。 現在國內很多廠商還是把客戶當笨蛋,喜歡弄成 out-of-the-box installation。 這會使得你主管會要求你把所有相依套件都編譯好包在一起,然後發佈給客戶。 這種情況下即使你選用的是 LGPL,還是會帶來一些麻煩。
descent: 為用而用和因為努力學了這技巧而硬要使用這段, 09/09 09:34
descent: 心有戚戚焉 09/09 09:35
mabinogi805: 推心態~ 順便體悟心靈祥和(別 09/09 09:52
hn12404988: 學習了 09/09 10:48
BlazarArc: 推 09/09 11:01
※ 編輯: tinlans (220.132.55.117), 09/09/2016 12:07:59
legendmtg: <(_ _)> 09/09 13:37
Chikei: 推心態,但是這心態跟DP一樣,沒撞過真的很難知道多重要XD 09/09 16:20
bluesoul: design pattern不就是一種自然而然的東西嗎?他只是歸 09/09 19:45
bluesoul: 納出各種情境下會使用的適當架構 09/09 19:45
bluesoul: 為何還會有優越感或是造神 09/09 19:46
這種畸形的現象一直存在,我身邊大概幾年洗一次新血就會 loop 一次。 他們追求的是寫出一行神奇程式碼,或者一小段神奇程式碼。 這一行或一小段可以做很多事,威力強大,而且身邊看得懂的人少。 每當他們接觸到一些進階名詞,就會興高采烈地跑來問我有沒有聽過。 當他們發現我居然大部分的都聽過用過後,就開始漸漸覺得我很厲害... 但是一旦跟我開始共事,發現我寫的 code 平凡無奇,無法帶給他們驚喜時, 就會開始大失所望,說什麼「怎麼連你寫的 code 都這麼普通?」 一開始我也不懂為什麼我不能寫普通的 code,但後來才明白他們學技術是跟人炫耀用的。 他們期待跟我共事可以偷學到更多驚人的奇特技巧,好跟更多人炫耀很少人知道的絕招。 結果我只是在該寫 loop 的地方寫 loop,該 call function 的地方 call function, 他們覺得一點都不新鮮,覺得這種程式幼稚園都寫得出來,很快就對我覺得膩了 XD 我常覺得這類人的志願應該不是想當程式設計師,而是想當魔術師吧...
CoNsTaR: 樓上 你從來沒有感覺到 c++ 社群崇尚技術大於一切的風氣 09/09 20:15
CoNsTaR: 嗎 09/09 20:15
CoNsTaR: 不論是書籍還是網路 普遍看來都是這樣的吧… 09/09 20:15
※ 編輯: tinlans (220.132.55.117), 09/09/2016 21:06:47
Sirctal: 我很好奇 CoNsTaR大 可以推薦一下 C++的社群嗎?? 09/09 21:10
james732: 推心得分享,覺得自己也有犯過這種錯誤 Q_Q 09/09 21:24
sa074463: 推,我寫不了厲害的只能寫普通的XD... 09/09 22:20
bluesoul: 看懂的人少是一個很危險的訊號,代表程式可讀性很差, 09/09 22:33
bluesoul: 偏偏這又是大型軟體相當重要的一環 09/09 22:33
bluesoul: 好的程式碼應該是易讀,易懂,好維護,好擴充,功能正確 09/09 22:34
bluesoul: ,沒有顯而易見的效率問題 09/09 22:34
在台灣,看得懂的人很少,可以代表兩種完全不同的狀況: 1. 程式可讀性真的很差。 2. 團隊裡的成員程度偏低,大部分書上有寫的東西都不知道。 在一知半解的狀況下沈醉在進階技術的人,很大比例會主觀認為大家看不懂的原因是 2。 雖然事實上有過半比例也的確是 2,但如果沒比他懂的人把他導向正途, 那麼 1 的比例也會漸漸變高,因為他已認定別人看不懂是別人程度低。 就拿 strategy pattern 來講好了,對於從來沒學過這概念的人,他們會認為這種程式: 1. control flow 到處跳來跳去,看得頭昏眼花。 2. 要同時開好多檔案 trace 才能知道程式在做什麼。 3. 這種事情明明直接寫 switch case 就好了,幹嘛那麼麻煩,弄一堆 classes。 接著這兩種人就會戰起來。 如果懂的這方又是個性很差、不愛解釋的類型,狀況就會更糟。 很容易會演變成自認為高處不勝寒,大家都太混、不知長進,所以無法懂他。 之後就會選擇脫離,另外找懂得賞識他的地方,然後四處 loop 同樣的問題。 說實話,我覺得這種人學這些技術也是白學了。 因為這些技術當初發展出來的動機跟目的,就是為了溝通跟合作。 這些概念的本質要解釋其實並不難,直接嗆人書看太少,實在是很沒必要。 如果學會這些技術,卻做不到基本的溝通,那我也只能說這種人不適合幹這行。 就像前面說的,這樣的人大概比較喜歡當魔術師,大家看他一個在舞台上表演就好了。 國內教育沒救,大家程式都靠自學,導致很少人循正規途徑接觸軟體開發技術。 以物件導向來說,大多數的人只能從 coding 角度去理解,沒有從本質上去體會。 學習物件導向,實際上需要搭配 UML,還要搭配一套軟體開發流程。 而這個軟體開發流程,傳統上就是以學院派的 Unified Process (UP) 為主體進行教學。 在學習 UP 的過程中,會慢慢熟悉 UML 的運用,以及理解 OOA 跟 OOD 的基礎。 當然 UP 除了這些之外,還有很多會出現在系統分析與設計、軟體工程等課本內的東西。 整套學下來以後,才輪到 GoF design patterns,講白了它是 OOD 流程的 library。 design patterns 是 design 流程的 library。 design patterns 是 design 流程的 library。 design patterns 是 design 流程的 library。 因為很重要,所以講三遍 XD 連 OOD 到底在幹嘛、OOD 之前和之後又在幹嘛都不明白, 然後只是聽了一些演講或看網路文章,讓他覺得 OOD library 很神奇很強大, 於是連 OOP 的概念都還只摸到皮毛,就跳去使用 OOD 的 library, 這究竟會有什麼下場,我想不少人已經看過榜樣了。 其實要過濾這樣的人並不困難,從本質面來問就行了。 像是我遇到有人自稱物件導向超強,熟悉 design pattern,那我可能會問: 「請盡可能詳述 inheritance 和 composition 的差異及優缺點。」 「物件導向當中的 delegation 是什麼?意義為何?」 大部分的情況下,對方都會從 coding 面來回答,寫 code 舉例,然後就沒了。 進一步引導對方做本質性的解釋,對方也只會從特定語言機制來做功能說明。 問對方以往在開發程式上使用這些技巧是基於什麼想法,為何如此選擇,也都一樣。 對方從頭到尾都只會著眼在 code 如何撰寫,除此之外沒有其它的了。 如果怎樣換方法問都一樣,那就可以斷定這個人就是不會。 自稱會 design patterns,卻開口閉口都只談及 code,那只不過是自以為會。 至於要不要用他,就得聊聊其它東西,看這人是不是可造之材。 講到這麼遠,可能會有人以為我主張使用 OOP 要先精通 OOA 跟 OOD,但並非如此。 實際上就和吸收一些科普知識一樣,只是要有個全局觀,概略瞭解 OO 世界的全貌就好。 原本學校的功能就是讓學生學到概論性質的東西,之後有興趣的人再自行深入。 不過當前國內的學校連 overview 都沒辦法教給學生,導致各種亂象叢生。 前面雖然我提到了 UP,但它也只是幫助人理解整個物件導向世界的一環而已。 現實世界除了超大型公司以外會使用它的進階改良版外,並沒有什麼地方會用上它。 中小型團隊如果有引進類似概念,那可能是 Agile UP (AUP),算是它的精簡版。 名字裡沒有 UP 的各種開發方式也很多,但這些都不重要,很容易觸類旁通。 那些名詞換來換去,用到的概念跟工具其實都還是不會差太多。 對完整又繁雜的學院派 UP 有個概略瞭解後,接觸敏捷法家族的流程也比較容易上手。 至於那種對 design 毫無概念的 coder,手上直接拿著一本敏捷程式開發在讀... 那又是另一個故事了,日後有機會再提 XD
Sirctal: 我覺得除非某些關鍵地方真的是效能大於一切 09/09 22:38
Sirctal: 而且不是很常變動的地方再來用這種技巧會比較好 09/09 22:39
Sirctal: 個人淺見 09/09 22:39
template 實際上還能帶來鬆散的耦合性,賦予 programmer 更高的自由度。 當 user 是 programmer 的時候,著眼點會跟以往有很大的差別。 或許你吸收過一些知識,認定常見的 strategy pattern 實現方式耦合度已經夠低了。 但那只是相對於最強耦合度的繼承而言,事實上純 OO 的合成和聚合關係耦合度仍偏高。 更何況在合成關係的對面,其實還是一個又一個的繼承體系,到頭來只是鬆綁了一點。 template 會將關係從 2D 的世界拓展到 3D,增加了一個維度,從平面變成立體。 在這麼廣闊的世界裡,就算出現繼承關係,只要使用得當,甚至能讓你感覺不到被綁住。 雖然聽起來很不可思議,但實際接觸到以後就會恍然大悟。 你的 user 不再被強迫去繼承什麼,只要遵照協定提供對應介面,使語法有效即可。 如果你接觸過泛型程式設計的基礎,應該聽過一個概念叫有效算式 (valid expression)。 不用懷疑,這裡講的協定和語法有效指的就是圍繞在有效算式上的概念。 「使語法有效」是一個很活的概念,遠比「繼承介面並實作」還靈活。 身為一個 library 的設計者,你的 user 是 programmer。 programmer 最討厭被綁住,你的 library 對他的制約能越少他越高興。 如果你希望在給他自由的同時,又能提供防呆機制,那你就必須踏入 TMP 的領域。 這邊再說下去已經不是網路文章可以承受的篇幅了。 如果沒有讀過 Modern C++ Design,可以弄一本來讀讀看,把第一章讀完就好。 要是你對後面的部分還有興趣,再讀下去也無妨。 不過後面的東西在 C++11 問世之後價值下降不少,當然只是學個概念的話就無所謂。
CoNsTaR: 沒有特別指什麼啦…只是泛指網路上的 c++ 使用者們而已 09/09 23:08
其實也是要看狀況判斷。 如果是 library 設計者,通常背後有許多局外人難以參透的理由,不是在賣弄技術作樂。 當年 SFINAE 這特性被拿來玩,然後做出什麼 enable_if<> 來, 很多局外人看了也只覺得這群 C++ 阿宅已經玩 C++ 玩到發瘋該去看看醫生了 XD
EdisonX: 推 09/10 05:05
※ 編輯: tinlans (220.132.55.117), 09/10/2016 09:22:26
chchwy: 推推 09/10 08:19
Sidney0503: 該loop就loop 該call就call這多難?甚麼時候是那個"該" 09/10 09:18
Sidney0503: 別人的設計就算是迴圈和函數 有時候還是要問本人 09/10 09:18
Sidney0503: 有的是說未來好增加功能 有的是說好替換... 09/10 09:19
Sidney0503: 這種判斷要磨多久才能練成XD 09/10 09:19
Clangpp: 感動 我現在就在拜讀 Modern C++ Design 只是想問說 他 09/10 11:36
Clangpp: 後來有沒有繼續出 C++14甚至C++17的版本?? 09/10 11:36
不要期待比較好,作者已經轉戰 D 語言去了。 其實整本讀下去也什麼大礙,只是裡面一些工具已經在 C++11 有內建在標準。 如果你已經熟悉 C++11 的全部,就一邊看一邊自己轉換過去就好。
Clangpp: 像C++ Templates: The Complete Guide作者已經預告要出 09/10 11:37
Clangpp: C++17的版本了 09/10 11:38
final01: t大完全是另一種境介了,我還停留在努力學很神氣的東西亂 09/10 15:48
final01: 用的程度....這是必經之路? 09/10 15:48
學了當然就要試用,不練習用看看也學不到什麼東西。 但是如果你要在正式的場合拿那些技術來用,你得有個好理由說服自己。 開發單一程式,傳統的 OOP 就已經夠強大了,除非你想把內部的工具一般化。 但是把內部的工具一般化,到底有沒有一般化的必要,有沒有一般化的迫切性, 這些是在專案進行當中你要自己去評估的,如果沒有,你只能在有足夠餘裕的時候去做。
EdisonX: @final01,總比什麼都不學莫名的神氣來得好多了。 09/10 21:22
james732: 哇Template那本出C++17版我會想買 09/10 21:57
Clangpp: http://www.josuttis.com/tmplbook/faq.html FAQ有寫 09/10 22:08
※ 編輯: tinlans (220.132.55.117), 09/10/2016 22:29:14
ronin728: 很多學 C++ 的人都喜歡玩弄花招,蔚為風氣 09/14 10:19
Sirctal: 可是要看需求阿 如果需要就是要用啊XD 09/14 23:10
Sirctal: 就像很多飛行員喜歡賣弄飛行技巧 平時沒用 但是空戰來時 09/14 23:11
Sirctal: 搞不好就真的有用了 09/14 23:11