Clean Code: Function 函式
Chapter 3: Function
- 
	簡短! 關於函式的首要準則就是簡短,第二項準則就是要比第一項的簡短函式還要更簡短。 
- 
	區塊(Blocks)和縮排(Indenting) If、else、while及其他描述都應該只有一行,而那行通常是函式呼叫描述。不僅能維持封閉函式的簡短,區塊內呼叫的函式名稱也能來描述意圖。 public static String renderPageWithSetupsAndTeardowns(PageData pageData, bool isSuite) { if (isTestPage(pageData)) includesSetupAndTeardownPages(pageData, isSuite); return pageData.getHtml(); }
- 
	只做一件事情 函式應該只做一件事情 
- 
	函式的段落 如果一個函式被拆成幾個段落,包含宣告區 declarations、初始區 initializations、 過濾區 sieve,這是做超過一件事情的徵兆 
- 
	每個函式只有一層抽象概念 
- 
	由上而下閱讀程式碼:降層準則 我們希望在閱讀程式碼時,能夠像閱讀一連串TO段落,每個段落描述著目前所處的抽象層次,並且提及了接續的下個層次的TO段落 
- 
	Switch描述 Switch函式問題一可能會太冗長,問題二涵式可能做超過一件事情,問題三可能違反單一職責原則(SRP),第四點當新型態加入後,函式必須改變所以它違反了開放封閉原則(OCP) 作者認為可以使用switch描述來產生Emplyee介面(interface)的實體(instance),另外,其他不同的函式都藉由介面,透過多型的方式來指派,例如calculatePay(計算薪水)、isPayDate(是否為發薪日)、deliverPay(發薪水) 
- 
	使用具描述能力的名稱 當每個你看到的程式,執行結果都跟你想得差不多,你會察覺到你正工作在Clean Code上 維持命名的一致性,在你的模組裡,使用一致的片語、名詞和動詞來替函式取名 
- 
	函式的參數 最理想是零參數函式,其次是一個,盡量避免使用三個參數。 
- 
	單一參數的常見形式 1.與這個參數有關的問題,例如 boolean fileExists("MyFile") 2.對參數進行某種操作,例如 InputSteam fileOpen("MyFile") 你應該選擇能明顯區分這兩種的名稱,如果一個函式會轉換輸入的參數,則轉換後的產物應該出現在回傳值裡 
- 
	兩個參數的函式 writeField(name) 比 writeField(outputStream, name) 更容易理解 outputStream.writeField(name) 是更好的方式
- 
	三個參數的函式 使用者需反覆閱讀查看才能理解 
- 
	物件型態的參數 Circle makeCircle(double x, double y, double radius); Circle makeCircle(Point center, double radius);可以使用類別來統整概念相似的參數 
- 
	動詞和關鍵字 函式和參數要形成一個動詞/名詞的良好配對,例如寫入名稱write(name),name會被寫入,或writeField(name)更能告訴我們name是一個欄位(field)。 關鍵字式的命名,例如 assertEquals -> assertExpectedEqualsActual(expected, actual),減輕了需記住參數的負擔 
- 
	要無副作用(side effects) 你的函式保證只做一件事,但卻暗地裡偷偷做了其他事情 public class UserValidator { public boolean checkPassword(string userName, string password) { User user = UserRep.findByName(userName); if (user != null) { if (user.password == password) { Session.initialize(); //side effects 使用者並不知道此函式內會改變Session,這是有風險的 //,除非將函式名稱修改為checkPasswordAndInitializeSession return true; } else { return false; } } } }
- 
	輸出型的參數 要避免使用輸出型參數,如果函式必須要改變物件的某種狀態,就讓該物件改變其本身的狀態吧 public void appendFooter(StringBuffer report); -> report.appendFooter();
- 
	指令和查詢的分離 將指令Command和查詢Query分開,避免模稜兩可的情形。 public boolean set(String attribute, String value); if (set("username","unclebob")) ... //無法判斷是詢問username被設為unclebob或是將username設為unclebob並回傳... //應該修改為 if (attributeExists("username")) { setAttribute("username","unclebob"); }
- 
	使用例外處理取代傳回錯誤碼 //將指令函式當作判斷表達式用 if (deletePage(page) == E_OK) //可能回導致更深層的巢狀結構 if (deletePage(page) == E_OK) { if (registry.deleteKey(page.name.makeKey()) == E_OK) { logger.log("page deleted"); } else { logger.log("configkey not deleted"); } else { logger.log("deleteReference from registry failed"); } } else { logger.log("delete failed"); return E_ERROR; }try { deletePage(page); registry.deleteReference(page.name); configKeys.deleteKey(page.name.makeKey()); } catch (Exception e) { logger.log(e.getMessage()); }
- 
	結構化程式設計 每個函式裡的區塊,都應該只有一個進入點跟一個離開點,迴圈內不能有任何的break或continue描述,且永遠不可以有goto描述 將函式分開、重新命名、減少重複、縮短方法並重新安排方法的順序,有時候打散整個類別,並持續保持單元測是可以通過 
- 
	不要重複自己 重複程式碼也許是軟體裡所有的邪惡根源,物件導向就是一種減少重複程式碼的策略