Clean Code - 函式
大家好,我是 Cindy,相信看過 Clean Code - 無瑕的程式碼 這篇文章的朋友們已經知道什麼是 Clean Code 了,之後會陸續針對每個章節做重點整理,以龜速進行中。
無瑕的程式碼-敏捷軟體開發技巧守則 (Clean Code: A Handbook of Agile Software Craftsmanship) 第三章函式,我想就我們寫 Ruby 而言其實可以對應到我們所謂的方法。
簡短!
關於函式的首要準則,就是要簡短。第二項準則,就是要比第一項的簡短函式還要更短簡短。看到書中這段話有相當深刻的感受,當我們在看一段很長的函式(或方法),其實真的很常會看到不知道在哪裡,懷疑人生。另外想跟大家提一下 Sandi Metz’ Rules For Developers thoughtbot 的這篇文章,其中第二條規則 Methods can be no longer than five lines of code.,跟 clean code 這本書提到的簡短其實都是一樣的道理。希望每個函式(或方法)都一清二楚,透露出本身的意圖。
只做一件事情
函式應該做一件事情。他們應該把這件事做好。而且他們應該只做這件事。
如果函式只做了函式名稱下同一層抽象概念的幾個步驟,那麼,這個函式就算是只做了一件事。觀察函式是否超過一件事情的另一種方法,是看你是否能夠從此函式中,提煉出另一個新函式,但此新函式不能只是重新詮釋原函式的實現過程(實作)而已。
函式的段落
做一件事的函式沒有辦法被合理的分成不同段落。
由上而下閱讀程式碼:降層準則
我們希望程式的閱讀就像是由上而下的敘事。我們希望每個函式的後面都緊接著下一層次的抽象概念,如此,我們在閱讀程式可依照看到的一連串函式,對應著抽象層次降層閱讀。這個方法就叫做降層準則。
Switch 敘述 (或 if/else)
Switch 敘述總是在做 N 件事情,雖然我們無法避開使用 Switch 敘述,但我們能確保讓每個 Switch 敘述都被深埋在較低層次的類別裡,而且他永遠都不會被重複使用。我們可以利用多型(Polymorphism)來達到這樣的目的。
關於 Ruby 如何利用多型(Polymorphism)來達到這樣的目的,可參考 hafentalks #7 - Sandi Metz: “Go Ahead, Make a Mess”。
使用具描述能力的名稱
當每個你看到的程式,執行結果都與你想的差不多,你會察覺到你正工作在 Clean Code 之上。
別害怕去取較長的名稱,一個較長但具描述性質的名稱,比一個較短但難以理解的名稱還要好。
函式的參數
函式的參數數量,最理想的是零個(零參數函式;niladic),其次是一個(單參數函式;monadic),再不然就是兩個(雙參數函式;dyadic)。可以的話,盡量避免使用三個參數(三參數函式;triadic)。如果要使用超過三個參數(多參數函式;polyadic),必須有非常特殊的理由—否則無論如何都不應該如此做。
單一參數的常見形式
- 會問與這個參數有關的問題。
- 對這個參數進行某種操作。
- 事件。這類的形式比較少見,在這類型中會有輸入型參數,但是不會有輸出型參數。
旗標(flag)參數
使用 flag 參數是一種很爛的做法。將一個布林變數傳遞給函式,是一種非常恐怖的習慣。這馬上會使得方法的署名(signature)變得複雜,等同於大聲宣布此函式做了不只一件事。
物件型態的參數
當一個函式看起來需要超過兩個或三個的參數時,很可能需要將當中的一些參數包裝在一個類別裡。利用建立物件的方式,減少函式參數的數量,當一堆變數一起被傳遞時,他們是某個概念裡的相似部分,而這個概念應該獲得一個屬於他的名稱。
動詞和關鍵字
替函式選一個好名稱,可以產生許多良好的附加價值,例如解釋函式的意圖、解釋函式參數的順序性及意圖。
要無副作用
副作用(Side effects)就像是謊言。你的函式保證只做一件事情,卻暗地裡偷偷做了其他事情。有時候會使得同類別的其他變數,產生不可預期的改變。常會導致奇怪的時空耦合(temporal coupling)和順序相依性的問題。如果你必須有一個時空耦合,你應該在函式的名稱中說明清楚。
指令和查詢的分離
函式應該要能做某件事,或能回答某個問題,但兩者不該同時發生。
錯誤處理就是一件事
當你使用例外處理,而非使用錯誤碼時,新的例外可由例外類別衍生出來,不必被迫重新編譯和重新部署,就可以加入到現有的程式中。
不要重複自己 DRY(Don’t Repeat Yourself)
重複會讓程式變得擁擠,當演算法某些地方需要改變時,這些修改的地方需要花費 N 倍的工夫,因此也讓遺漏而導致的錯誤,發生的機會變 N 倍。
重複程式碼也許是軟體裡所有邪惡的根源。許多準則或慣例都是為了控制或移除他而發明的。例如,資料庫的 Codd’s normal forms(柯德正規法)是用來消除資料的重複。又例如,物件導向程式設計是利用將程式碼集中到基本的類別裡,來避免冗餘(redundant)。結構化程式設計、剖面導向程式設計(Aspect Oriented Programming)、元件導向程式設計(Component Oriented Programming)等。
結構化程式設計
每個函式,及每個函式裡的區塊,都應該只有一個進入點及一個離開點。要遵守這個準則,代表在一個函式裡,只能有一個 return 敘述,迴圈內不能有任何的 break 或 continue 敘述,而且永遠不能有 goto 敘述。
總結
今天的重點整理就到這邊了,還記得我曾經寫過指令和查詢混在一起的方法,被公司 Fred 大大告訴我不應該這樣寫,之後也在 clean code 看到一樣的概念,覺得熟悉,但確實我應該讓方法的命名可以直接看出來是做什麼,如果是要問他是什麼狀態,就不應該邊問邊更改他的狀態啊啊啊。