做出改變如此容易,導致程式設計過程中疲於應付變化。多年來的事實已經證明,凍結變化是不可能也是不切實際的想法,因而,必須尋找能夠應付改變的設計原則。

然而,看過這麼多的原則,真的能讓程式碼更完美嗎?還是只想來首五人電子樂隊(Five Man Electric Band)的〈Signs〉(https://goo.gl/dXHdAz)吶喊一下呢?

物件導向設計五原則

在《jQuery in action》的〈The jQuery plugin authoring guidelines〉中,以五人電子樂隊的〈Signs〉歌詞寫道:「標示!告示!到處都有這些提示!大煞風景,傷了我的心。這樣作!別那樣作!你是看不懂提示嗎?」作為開場,用以反映現今軟體設計原則如此之多,令人生厭。

到底是為了什麼,我們需要這麼多原則?稍微注重設計的程式人,談到原則最不陌生的,大概就屬Robert C. Martin(Bob大叔)提出的硬(SOLID)道理,SOLID分別是SRP、OCP、LSP、ISP、DIP的首字縮寫詞,而後面這五個又分別是Single Responsibility Principle、Open-Closed Principle、Liskov Substitution Principle、Interface Segregation Principle、Dependency Inversion Principle,程式人真的很愛縮略詞!

對於這五個原則,網路上搜尋「SOLID 設計」,就會有看不完的資料,每篇看起來都很有道理。也有人說到,程式寫太少,看過的程式太少,就無法體會這些原則的重要,而稍微寫過些程式,對程式有些痛苦經驗的,反而會對這些原則抱以崇拜,至於程式設計經驗豐富的人,則又認為這幾個原則不過就只是常識,就像覺得設計模式,不過就是取了些名字的東西罷了。

要是遵守原則就能避免混亂,就好了,不過仔細看看,這些原則像是在許願,試問,哪個程式會不希望相依物件改變時,不影響自身?誰又會希望某個介面改變,自己也被迫修改?更換另一個次型態物件時,當然會希望原程式行為沒有變化,新增功能時,能不改既有程式,也是每個人都想要的結果,萬不得已必須修改程式時,最好只改一個地方就好。

既然如此,我們的需求「改變」中,會有這五種造成程式「改變」的原因嗎?若沒有改變的原因,抱著五大原則檢討程式碼能否應付改變,感覺就挺空洞的!

簡單設計四原則

空洞!大概就是死抱著SOLID原則,試圖完美遵守的感受,問題就在於,不會有完美的設計。

如果對於SOLID原則生厭,或許可以看看Kent Beck在eXtreme Programming提倡的〈Xp Simplicity Rules〉(http://goo.gl/mM4R7J),其中對於簡單的程式碼列出了四個目標:「通過所有測試」、「能表達出我們想表達的所有意圖」、「每件事只說一遍而且只有一遍」、「沒有多餘的部份」。與SOLID著重在面對「改變」要有足夠的彈性相比,Kent Beck的簡單設計四原則,側重在完成既有的目標,並擁有未來面臨修改時的「本錢」。

這大概就跟面對感冒時,真正重要的是訴諸身體的抵抗力,是一樣的道理,若感冒病毒真的入侵了,那麼重點就在於身體要有本錢抵抗病毒。既然需求變化免不了,在針對需求做出程式碼上的變化時,就要有本錢迎接變化,要有能測試的程式碼來告訴我們,既有的程式功能被破壞掉了什麼;既然要改變程式,那麼就要能看懂既有程式的意圖,才知道如何做出修改;程式碼不重複,遇到要修改時,才不用到處搜尋、取代、新增程式。

至於「沒有多餘的部份」,則是〈You Arent Gonna Need It〉(http://goo.gl/Jwzw24)中,寫到的「在真的有需要時實作,而不是預期會有需求而實作」。

而且,其中說到,這除了節省時間之外,也可以避免「猜測」這件事,污染了程式碼,這大概跟別吃多餘的營養品來做「加強身體抵抗力」的這件事,是類似的道理吧!

說是簡單設計四原則,要完成這些目標,可真的不簡單,測試本身是門學問,程式碼要怎麼寫才能被測試,測試程式本身該怎麼寫,採用哪種測試方法論等,都得有十足的學習與經驗,如何撰寫出清楚、可讀的程式碼也是件難事,為此,或許你得研讀Bob大叔的《Clean Code》,或相關的書籍,想要程式碼不重複,視情況也有多種不同的實作方式,有時重複也沒那麼簡單可以看出來,有時狀態或行為上的不重複,也是得考量在內。

重構的三次法則

在簡單設計四原則中的「每件事只說一遍而且只有一遍」,有著另一個比較多人熟悉的名詞DRY,也就是Don't Repeat Yourself,或者另一個相對的名詞WET,也就是Write Everything Twice。

DRY一詞,目前認為最初出現在《The Pragmatic Programmer》中〈The Evils of Duplication〉,當中也將重複大致區分為四個類型:被迫(Imposed)、不經意(Inadvertent)、沒耐心(Impatient)與開發者之間(Interdeveloper)的重複。

被迫的重複是指那些因為專案環境的關係,導致某些資訊不得不重複地出現在選用的標準、技術或工具之中;不經意的重複,書中則舉了Line物件具有start、end、length屬性,其中length就屬於不經意的重複,因為length可藉由start、end求得;沒耐心的重複,大家較為熟悉,往往是因為沒時間、沒耐心或想抄捷徑,而用了複製、貼上、修改的做法;開發者間的重複,則是最複雜、最難以察處理的,不慎寫出具有相同功能,甚至是具有相同Bug的的程式碼等,都是可能的情況。

實際上,重複的類似也不僅止於此,重點在於面對重複,並不是立即奉行DRY這麼簡單,去除重複有時需要的成本極高。當去除重複後,發現被提取出來的部份,也只被用上過這麼一次,因此,有人寧可採用笨一點的方式,也就是採用KISS(Keep It Simple, Stupid)原則,認為某些時候去除重複是YANGI(You Arent Gonna Need It)!

這麼說來,DRY與KISS有時是有衝突的。在《Refactoring》書中〈When Should You Refactor?〉談到Don Roberts提出的三次法則(The Rule of Three):「第一次做某件事時只管去做;第二次做類似的事會產生反感,但無論如何還是做了;第三次再做類似的事,你就應該重構。」

在Derick Bailey的〈Abstraction: The Rule Of Three〉(https://goo.gl/B0WEhW)中,也提到三次法則有助於模式的察覺,像是數學上,1, 2, _, _, _, _很難判定數列後續數字為何,然而1, 2, 3, _, _, _的話,就可能推斷出後續是4, 5, 6的模式。

你的程式碼STUPID嗎?

我們看過幾個原則?SOLID、DRY、WET、KISS、YANGI、The Rule of Three,你還有沒有聽過Law of Demeter?在《Clean Code》中的說明是「只跟朋友說話,不跟陌生人聊天」,並舉了Apache框架中的程式碼ctxt.getOptions().getScratchDir().getAbsolutePath(),這種取得物件細節後又取得下個物件細節的做法,就違反了Law of Demeter,這會使得目前物件依賴於其他物件的內部結構。

那麼,這麼多原則的提出到底是為了什麼?在〈Don't be STUPID: GRASP SOLID!〉(http://goo.gl/iXJw6V)提出了另一個角度的看法「抱歉,但你的程式碼就是STUPID!」STUPID代表了Singleton、Tightcoupling、Untestability、Premature Optimization、Indescriptive Naming 、Duplication,對了,GRASP也是一堆原則的縮略詞!

當然,蠢程式並不只有這些問題,作者也承認STUPID是特意搞出來的名詞,不過,或許能解釋這麼多原則,不是讓程式碼更完美,而只是希望我們能看出自己的程式碼中犯了哪些蠢事,才會導致後續缺少彈性、無法測試、難以閱讀、到處充滿重複的程式碼,如果看著這些原則時,能夠嘖嘖稱是,那大概是過去曾經犯過不少蠢事了吧

專欄作者

熱門新聞

Advertisement