Clean Code 無瑕的程式碼 第3章 函式
第三章的主題是函式,第三章可以給讀者有很大的幫助。第三章後半部 Try/Catch 的部分可與第七章錯誤處理對照一起閱讀。
3-1重構之後變成3-2,3-1與3-2 比較起來3-2寫得比較簡短易懂。
簡短!
函式要越短越好,3-2重構之後變成3-3。
Listing 3-3
public static String renderPageWithSetupsAndTeardowns(
PageData pageData, boolean isSuite) throws Exception {
if(isTestPage(pageData))
includeSetupAndTeardownPages(pageData, isSuite);
return pageData.getHtml();
}
區塊(Blocks)和縮排(Indenting)
If、else、while及其他敘述都應該只有一行。
函式不應該大到包含巢狀結構,函式裡的縮排程度不應該大過一或兩層。
只做一件事情
等同單一職責原則,函式應該只做一件事。
如何判斷只做一件事?
從「抽象概念」判斷是否只做一件事,3-1有不同層次的抽象概念,3-2有兩層抽象概念,3-3已經無法再提取新的函式。
函式的段落
4-7 generatePrimes函式有幾個段落,宣告區、初始區、過濾區。這是做超過一件事的徵兆。
每個函式只有一層抽象概念
3-1程式沒有在同一層抽象概念
getHtml() (高層次)
String pagePathName = PathParser.render(pagePath) (中層次)
.append(“\n”) (低層次)
抽象概念與細節混在一起,會讓程式碼變得更複雜。
由上而下閱讀程式碼:降層準則
程式閱讀就像由上而下的敘事,每個函式後面緊接下一個層次的抽象概念。
將函式維持在單一層次的抽象概念。
讀者可以想一想「階層式樹狀圖」就能夠理解這段內容。
Switch 敘述
寫程式無法避免Switch,3-4是常見的情況,新增一個狀況的時候就新增一個case。
作者認為這種有下列缺點
太過冗長
函式做了超過一件事
超過一個理由可以改變函式,違反單一職掌原則(SRP)
新型態加入後,函式必須改變,違反開放閉合原則(OCP)
其他地方有相同的Switch case 結構
有兩個方法可以解決Switch case引發的問題多型(Polymorphism)與讓每個switch敘述埋藏在較低的抽象層次的類別,而且不會被重複使用。
3-5 將Switch埋在抽象工廠(ABSTRACT FACTORY)底下,產生Employee介面的衍生實體,不同的函式透過多型來指派。
使用具描述能力的名稱
函式名稱要能夠正確表達函式的功能,函數名稱可與第二章對照看。
函式的參數
最理想的情況是零個參數,儘量避免使用三個以上的參數,參數過多會讓函數難以測試。
單一參數的常見形式
與這個參數有關的問題
對這個參數進行操作,然後回傳
輸入事件(event)
旗標(flag)參數
作者反對旗標參數(輸入true 或 false),違反單一職責原則,表示函式做兩件事情,應該將函式分成兩個參數。
兩個參數的函式
兩個參數的函式可讀性不如一個參數的函式。
平面上的點位point會出現兩個參數的情況,可以設法將兩個參數化為一個參數。
三個參數的函式
儘量不要出現三個參數以上的函數。
物件型態的參數
參數過多的時候,可將其中一些參數包裝在一個類別。
參數串列
部分情況參數沒有固定的數量,如同一個List。List會被視做一個參數。
動詞和關鍵字
函式名稱儘量使用動詞與關鍵字,可增加程式可讀性。
assertExpectedEqualsActual(expected, actual) 這種函數名稱寫法參數順序與意義一目瞭然。
要無副作用
函式只做一件事,副作用指函數還會做其他事情。
3-6 函式還會進行初始化的動作,函數名稱並沒有提到會做初始化的動作。
輸出型的參數
作者反對輸出型參數,會影響程式的可讀性。
例如 function( out p) 函數將計算的結果輸出到p 。
如果不看函式宣告,會不知道是輸出型參數。
物件導向程式設計出現以後,this扮演輸出型參數的角色。物件可改變本身的狀態,取代輸出型參數。
指令和查詢的分離
考慮以下函式
public boolean set(String attribute, String value);
使用的時候
if(set(“username”, “uncleb”))...........
指令與查詢混在一起,很難從呼叫中知道真正的意義
指令(command)與查詢(query)分開,修改物件狀態與回傳物件資訊要分開完成。修改函數名稱之後,能夠清楚表達真正的意義。
if(attributeExists(“username”)){
setAttribute(“username”,”unclebob”);
}
使用例外處理取代回傳錯誤碼
用Try/Catch 例外處理取代回傳錯誤碼
提取 Try/Catch 區塊
從範例可看出從原本的Try 區塊提取出新的函式讓程式整潔。
錯誤處理就是一件事
處理錯誤的函式不應該再做其他事。try在函式裡幾乎是函式的開頭,在catch/finally區塊之後也不應該出現其他程式碼。
Error.java 的依附性磁鐵
作者說明為何例外處理的寫法比回傳錯誤碼的方式好?
假設新增一種錯誤狀況
回傳錯誤碼的方法會修改列舉,會變成相關的類別必須重新編譯。
例外處理的寫法新的例外可由例外類別衍生出來,不必全部重新編譯。
不要重複自己
軟體發展領域的所有創新,都是為了消除原始碼的重複。
結構化程式設計
每個函式區塊都應該只有一個進入點與一個離開點。
一個函式只能有一個return敘述,迴圈內不能有任何break或continue敘述,不可以有goto敘述。
結構化程式設計對小函式幫助有限,對大函式才能展現出巨大的好處。
goto 敘述只有在大型函式才有用。
你要如何寫出這樣的函式?
寫程式如同寫文章,不可能「馬上好」只能「馬上漸漸好」,初稿總是雜亂無章,因為有單元測試程式,可以持續修改程式碼。
總結
函式是動詞,類別是名詞。
寫程式像是說故事。
留言
張貼留言