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 敘述只有在大型函式才有用。


 


你要如何寫出這樣的函式?


 


寫程式如同寫文章,不可能「馬上好」只能「馬上漸漸好」,初稿總是雜亂無章,因為有單元測試程式,可以持續修改程式碼。


 


總結


 


函式是動詞,類別是名詞。


 


寫程式像是說故事。


 


留言

這個網誌中的熱門文章

異世界NTR web版第三章 觀後感 喧賓奪主 ,反派實力過強

泛而不精的我被逐出了勇者隊伍 web第三章 觀後感 菲莉真能打; 露娜超爽der

持有縮小技能的D級冒險者,與聖女結婚並加入勇者團隊 漫畫 01-04 觀後感 大我與小我