l

2012年1月7日 星期六

亂談軟體設計之答客問:小鄭篇

January 06 21:42~23:05
January 07 18:29~18:32


Teddy 部落格的文章一向都跟「剛出爐的麵包」一樣,做好之後熱騰騰的馬上販賣(熱熱吃比較好吃)。Teddy 使用 google blogger 所提供的 web 版陽春編輯器當場寫完之後當場發表,但最近考慮到「美味關係宣言(2012 年每天至少要發表一篇文章)」,原本這種「每天供應現烤麵包」的策略可以需要改變一下,有時間的時候要先準備一點庫存,以免 Teddy 臨時有事,或是出外旅遊無法實現每天寫一篇的諾言。


明天(禮拜六) Teddy 白天都有事情要忙,晚上回家之後應該也沒力寫部落格了,所以今天(禮拜五)先偷跑一下,「把明天的文章,今天就傳便便(準備好)」,先先好存起來等明天早上再發布。


***


今天(一月六日禮拜五)有位名叫小鄭鄉民(Teddy 內心獨白:莉莉」還好吧...XD看了「亂談軟體設計(6):Single Choice Principle」之後留言問 Teddy:




小鄭問:
請教一下,這些原則基本上都是如此相似,基本原則就是要避免重複、發散,完成模組化的效果,為何需要發明出好幾個名詞呢?


我想了解的是,這些名詞似乎都可以在把握住模組化精神的前提下,隨著應用場景作出類似結論,實務上困難的往往是執行力與對所花心力的成本考量與取捨,有時甚至不易估算為達到某種彈性所花之心力是否合理。

舉例來說,為何需要OCP這個名詞?一般都會知道當要引進新功能時,如果還要修改既有代碼,那麼表示既有代碼就還有進一步模組化的空間,但往往是在開發中期(或後期)才因帶入新需求然後發現要提供額外彈性的地方,然後我們透過重構去增加此彈性。令我不解的是,用既有知識與邏輯就可以很好解釋的概念,為何要"新增"名詞呢?




Spirit Du 代答:
會有不同的名詞,是因為相同的想法在不同的context下有相似但不相同的表現,但不能因為相同的想法就用一個名詞代表所有「相似但不相同」的表現,不然就不知道是哪一種表現。



Teddy 答:
你說得很對,其實這些設計原則講穿了都是為了達到『模組化』的目的。

但是你說得「一般都會知道當要引進新功能時,如果還要修改既有代碼,那麼表示既有代碼就還有進一步模組化的空間」這一點 Teddy 並不同意。應該是你已經很有經驗了,所以直覺上會推導出上述的結論。但是對於很多初學設計的人,或是經驗不是那麼豐富的人,他們會認為,「要加新功能就是要改原本的程式啊,不然要怎麼加?」

所以,有了這個設計原則(例如 OCP),就好像是 design patterns 一樣,有一個 common languages for communication。作為溝通,教育,反思之用。只要大家都等這樣的原則,設計出來的程式品質也會比較相近。

不然的話,所有的設計原則最後只有一條「模組化設計」,其他的都可以省略了,但是這樣又太抽象啊。所以才需要根據這個最高指導原則(模組化設計)又衍生出那麼多設計原則。

其實 Dprint Du 已經幫回答得很好了:『會有不同的名詞,是因為相同的想法在不同的context下有相似但不相同的表現,但不能因為相同的想法就用一個名詞代表所有「相似但不相同」的表現,不然就不知道是哪一種表現。』雖然這樣的回答還是有點抽象...XD。




小鄭再問:
多謝解釋,我經驗還嫩,只是一直有興趣而已。:-)

BTW,其實我初看到 design patterns 時也有類似新增"名詞"的感覺 :P

或許將這些觀念形成語彙並成為開發者的共通語言主要就是為了溝通,但對初學者而言,如果不透過相當份量的實作搭配,只怕真的會變成原則與阻礙,而不是會在實務上應用的手法。

以新語彙提供概念然後在實作中印證比較好?還是隨著實作經驗引進最基礎的概念(模組化)比較好?或許因人而異,但我比較 prefer 後者的方式。






zwshen 插花:
這一切的一切,其實就是一個字:「道」。只要你得道了,到達形而上的境界,就不會侷限於這些「器」了。不過我想我還是需要好好硺磨這些「器」,希望有朝一日能得道啊...XD。

***


Teddy 想講得是,學設計原理和實做兩者並不衝突,反而是可以相輔相成。這有一點像是論語裡面所說得「學而不思則罔,思而不學則殆」,總之就是「要學也要思,邊學邊思,邊思邊學」。

Teddy 舉個切身的例子,在 「亂談軟體設計(5):Dependency-Inversion Principle」這篇文章中,Teddy  舉了個例子:





這個例子是從一個叫做 SyncFree 的開放原始碼軟體的設計圖中經過整理之後節錄出來的。Teddy 是這個軟體的第一代原始設計者(算一算又是 10 年前的事了...Orz),但是,上圖的設計(符合 DIP) 原本是長成這樣的:



說實話當年 Teddy 沒有把 DIP 看的很懂,因為實做 IFileManipulate 介面的類別都放在 Mechanism Module (Java package)裡面,所以很自然地 Teddy 就把 IFileManipulate 介面也放到 Mechanism Module 裡面。這樣的結果就變成,high level modules depend on low level modules,違反了 DIP 原則。

Teddy 是何時才發覺到原本的設計違反 DIP?是後來看了 Matin Flower 的「Inversion of Control」然後又想起之前看過的 DIP,又回頭把 DIP 再看一次,才發覺原來這個 IFilemanipulate 擺錯位置了。說真的,如果 Teddy 沒有看過這個(或是說,這些)設計原則,很有可能 Teddy 會一直維持原本的設計

***



最後剩下一個問題,Teddy 原本的設計就算是違反 DIP 好了,那又怎樣?咬我啊...嗯嗯...說真的,是不會怎樣,所以 Teddy 一直沒發覺有何不妥。因為原本的設計某種程度是有符合 Open-Closed Principle,只不過是把 IFilemanipulate 擺錯模組有那麼嚴重嗎?

假設今天有另外一個團隊,要開發新的實做,例如支援將檔案同步到雲端,於是這個團隊就需要去參考到 IFilemanipulate 介面。如果 IFilemanipulate 介面放到 Mechanism 模組,那麼模組相依關係就會變成:




Mechanism Module 2 變成相依於原本的 Mechanism Module,這樣的相依關係感覺就是有點怪怪的(因為某個底層的模組改變,而可能導致上層模組與其他的底層模組也改變了)。如果改成符合 DIP 的話:






這種相依性關係就變得比較「乾淨」。當專案裡面有很多模組的時候,模組之間的相依性管理就會變成一個很重要的課題。如果能夠把模組相依性控管的比較好一點,對於軟體的施工(同一個團隊內的派工或是切割給其他團隊去實做)、持續整合與建構、產生安裝程式與系統佈署等等都有好處。

***


至於何時要學這些設計原則呢,Teddy 也沒有正確的答案。之前 Teddy 在唸書的時候曾經跟研究所碩士班的學弟介紹過這些原則(3-6 個小時),一般的 programmers 應該只要學過基本的物件導向觀念也都可以理解。既然學這些設計原則花不了太多的時間,Teddy 還是會建議,有機會的話,早點了解也沒什麼不好,因為任何的設計技巧,都是需要時間「發酵(醞釀)」,沒人說要學一次就馬上學會啊,但早點接觸,在做案子的時候,可以激發一下利用機會驗證與釐清這些設計原則


***

友藏內心獨白:總之還是要多看書多寫程式

4 則留言:

  1. Hi Teddy

    沒想到您特地花一篇blog說明,真是感謝。:-)

    不過我的意思似乎說的不夠清楚。在前一篇blog的留言中,我最後有說的比較清楚,問題就是:何時是形成新語彙的恰當時機?

    為何我會對新語彙的產生有質疑?因為語言容易引導思考方式,並給予隱喻,但也因此需要心智上的負擔,所以這也是為什麼好的程式設計師幾乎把命名視為最重要的事情之一。從pattern hatching一書中,也可看出design patterns作者們對取名與挑選出具代表性的pattern有多謹慎的態度,雖然不到science的程度,但也很夠意思了。

    但是面對principle這樣需要更高抽象性與正交性的語彙,我卻發現提出這樣概念的作者們似乎沒有同樣的高度,使得類似的概念只是換一個名詞解釋,而不是建立起一個框架(類似design patterns一書)彼此呼應。

    我同意設計原理與實作應同時並進,但設計原理所引進的語彙需考量得更嚴謹些才是。

    回覆刪除
  2. 說得越抽象,就越接近zwshen說的境界XDDD

    回覆刪除
  3. Hi Spirit

    不知道你是我說的太抽象還是那些xxx principle?:-)

    Anyway,我只是對名詞的氾濫有點疲倦而已,許多可以用case study搭配基本邏輯解釋的想法,卻被許多名詞所佔領,我們究竟是讓複雜的事情變簡單,還是讓簡單的事情變複雜?我實在不知道對學生來說,到底這是好事還是壞事。-_-

    回覆刪除
  4. To 小鄭:不好意思,我沒說清楚,我不是針對你的留言,而是Teddy學長在文中說,我的解釋還是很抽象所開的玩笑。

    不過,對於名詞的氾濫,我倒覺得沒這麼嚴重,用use case說明不是不好,但會變成無窮盡,無法列舉所有的use case,這也是為什麼OO裡有個很重要的觀念是『抽象化』,use case都是concrete的example,design principle和design pattern之類的原則或設計,都是在某些context(經抽象化後的use case)下所提出的solution。事實上,世界上的事物就是很複雜,自然就會有很多『名詞』出現。

    如果覺得名詞很多很複雜,就把它想成:『每個名詞都是一個數學公式』,你可以不背它,不用它,但當你想破頭解決了一個問題後,可能會發現你的solution原來跟某個公式一樣,甚至更好或是更爛。如果更好,你就可以再提出一個新的公式(名詞),更爛,你會覺得為什麼我不早知道有個公式可以解決問題。

    p.s.想破頭解決問題很好,是一種學習的方法,但如果實務上在工作時,沒有那麼多時間想破頭,使用別人提出的方法也是一種快速的學習方法。

    回覆刪除