摘要:但大多數情況下應該盡量利用一些機制將二元函數轉換成一元函數。應該為起一個更能描述函數功能的函數名副作用在于對這個調用函數,顧名思義,就是用來檢查密碼。注釋及其描述的代碼之間的聯系應該顯而易見。受控異常的代價就是違反開放閉合原則。
大師級的程序員把系統當作故事來講,而不是當作程序來寫。有意義的命名 做有意義的區分
如果同一作用范圍內兩樣不同的東西不能重名,那其意思也應該不同才對。那么這兩樣東西應該取不同的名字而不是以數字區分。如果以下代碼參數名改為 source 和 destination,這個函數就會像樣許多
public static void copyChars(char a1[], char a2[]){ for(int i = 0; i < a1.length; i++){ a2[i] = a1[i]; } }
以數字系列命名(a1、a2、a3...)是依意義命名的對立面。這樣的名稱完全沒有提供正確的信息
Info和Data就像a、an和the一樣,是意義含糊的廢話。但有時候只要體現出有意義的區分,使用a和the這樣的前綴就沒錯
廢話都是冗余的。Variable一詞永遠不應當出現在變量名中。Table一詞永遠不應該出現在表名中
避免思維映射例如循環中的 i、j、k 等單字母名稱不是個好選擇;讀者必須在腦中將它映射為真實概念。最好用 filter、map 等方法代替 for循環
類名與方法名類名和對象名應該是名稱或名詞短語
方法名應該是動詞或動詞短語
每個概念對應一個詞例如 fetch、retrive、 get 表達同一個意思,應該選定一個,然后在各個類中使用相同的方法名。
別用雙關語避免將同一單詞用于不同的目的。同一術語用于不同概念,基本上就是雙關語了。
使用解決方案領域名稱記住,只有程序員才會讀你的代碼。所以,盡管用那些計算機科學(Computer Science,CS)術語、算法名、模式名等。
動詞與關鍵字給函數取個好名字,能較好地解釋函數的意圖,以及參數的順序和意圖。
對于一元函數,函數和參數應當形成一種非常良好的動詞/名詞形式。
// good write(name) // better // 更具體,它告訴我們,"name"是一個"field" writeField(name)
函數名稱的關鍵字(keyword)形式。使用這種形式,把參數的名稱編碼成了函數名
// bad assertEqual(expected, actual); // good // 這大大減輕了記憶參數順序的負擔 assertExpectedEqualsActual(expected, actual);函數 短小
函數第一條規則是要短小。第二條規則不是要短小。越短小越好,20行封頂
if、else、while等語句,其中的代碼應該只有一行。該行大抵應該是一個函數調用語句。因為塊內的函數擁有較具體說明性的名稱,從而增加了文檔上的價值
只做一件事確保函數不能被再拆分
參數最理想的參數數量是零,其次是一,再次是二,應盡量避免三
不要傳遞標識參數,標識參數大聲宣布函數不是做一件事。如果標識為 true 將會這樣,標識為 false 則會那樣
二元函數:有些時候兩個參數正好。例如 Point p = new Point(0, 0);因為點天生擁有兩個參數。但大多數情況下應該盡量利用一些機制將二元函數轉換成一元函數。例如,把writeField 方法寫成outputStream的成員之一
// bad writeField(outputStream, name); // good outputStream.writeFiled(name);
參數對象:如果函數看起來需要兩個、三個、或三個以上參數,就說明其中一些應該封裝為類了
// bad Circle makeCircle(double x, double y, double, radius); // good Circle makeCircle(Point center, double radius);
從參數封裝成對象,從而減少參數數量,看起來像是在作弊,但實則并非如此。當一組參數被共同傳遞,就像上例中的x和y那樣,往往就是該有自己名稱的某個概念的一部分
無副作用
確保函數功能就像函數名描述的一樣,不要做函數名描述以外的事情。應該為起一個更能描述函數功能的函數名
public UserValidator { private Cyptographer cyptographer; public boolean checkPassword(String userName, String password) { User user = UserGateway.findByName(userName); if(user != User.NULL) { String codePhrase = user.getPhraseEncodedByPassword(); String phrase = cyptographer.decrypt(codePhrase, password); if("Valid Password".equals(phrase)) { // 副作用在于對這個調用 // checkPassword函數,顧名思義,就是用來檢查密碼。該名稱并未暗示它會 // 初始化該次會話。可以重命名為 checkPasswordAndIntializeSession Session.initialize(); return true; } } return false; } }
普通而言,應該避免使用輸出參數,如果函數必須要修改某種狀態,就修改所屬對象的狀態
// bad // 讀者會弄不清s是輸入參數還是輸出參數 // 也會弄不清這函數是把s添加到什么東西后面,還是把什么東西添加到s后面 appendFooter(s); // 函數簽名 public void appendFooter(StringBuffer report){} // good report.appendFooter();分隔指令與詢問
函數要行做什么事( 例如 user.setName("xxx") )、要么回答什么事( 例如 user.isVip() )。一個函數里不要把兩件事都干了。
如何寫出好函數分解函數
修改名稱
消除重復
注釋 好的注釋法律信息
警示性注釋
TODO 注釋雖好,但也要定期查看,刪除不再需要的
壞的注釋循規式注釋。 例如每個函數都要有Javadoc或每個變量都要有注釋的規矩全然是愚蠢可笑的。這類注釋徒然讓代碼變得散亂
注釋掉的代碼。 現在已經有源代碼控制系統,不要的代碼應該立即刪掉
不明顯的聯系。 注釋及其描述的代碼之間的聯系應該顯而易見。注釋的作用是解釋未能自行解釋的代碼。如果注釋本身還需要解釋,就太遺憾了
切斷代碼間的聯系
// bad public class ReportConfig { // // The class name of the reporter listener // private String m_className; // //The properties of the reporter listener // private List格式 垂直距離m_properties = new ArrayList (); public void addProperty(Property property) { m_properties.add(property); } } // good public class ReportConfig { private String m_className; private List m_properties = new ArrayList (); public void addProperty(Property property) { m_properties.add(property); } }
變量聲名。 大多數情況下變量聲名應該盡可能靠近其使用的位置。但是在類內,變量聲名應該統一放在頂部,因為這樣讀者可以一眼看出這個類有什么變量。
相關函數。 若某個函數調用了另一個函數,就應該把它們放到一起,而且調用者應該盡可能放在被調用者上面。這樣程序就有個自然順序。
概念相關。 概念相關的代碼應該放到一起。相關性越強,彼此之間的距離就該越短
public class Assert { static public void assertTrue(String message, boolean codition(){} static public void assertTrue(boolean codition(){} static public void assertFalse(String message, boolean codition(){} // ..... }
這些函數有關極強的概念相關性,因為他們擁有共同的命名模式,執行同一基礎任務的不同變種。互相調用是第二位的。即便沒有互相調用。也應該放在一起。
更多例子查看 p79 -- 5.25 垂直順序
錯誤處理 抽離Try/Catch代碼塊函數應該只做一件事,錯誤處理就是一件事。
// bad public void delete(Page page) { try{ deletePage(page); registery.deleteReference(page.name); configKeys.deleteKey(page.name.makeKey(); }catch(Expection e){ logError(e); } } // good public void delete(Page page) { try{ // 將上例的操作,封裝到一個方法 deletePageAndAllReferences(page); }catch(Expection e){ logError(e); } }使用非受控異常
受控異常:Checked Exception(FileException、SQLException等),這類異常必須寫 try/catch,或者 throw拋出,否則編譯通不過。
非受控異常:Unchecked Exception,這類異常也叫做運行時異常(與非受控異常 字數相等),這類異常不需要 try/catch,也不需要 throw拋出。即使 throw 了,上層調用函數也非必須捕獲,編譯能通過。
受控異常的代價就是違反開放/閉合原則。如果你在方法中拋出受控異常,這意味著每個調用該函數的函數都要修改,捕獲新異常,或在其簽名中添加合適的throw語句。對于一般的應用開發,受控異常依賴成本要高于收益成本,盡量 try/catch 處理,不要拋出。
給出異常發生的環境說明應創建信息充分的錯誤信息,并和異常一起傳遞出去。在消息中,包括失敗的操作和失敗類型。如果你的應用程序有日志系統,傳遞足夠的信息給catch塊,并記錄下來。
依調用者需要定義異常類// bad ACMEPort port = new ACMEPort(12); try { port.open(); } catch(DeviceResponseException e) { reportPortError(e); logger.log("Device response exception",e); } catch(ATM1212UnlockedException e) { reportPortError(e); logger.log("Unlock exception",e); } catch(GMXError e) { reportPortError(e); logger.log("Device response exception",e); } finally { // ..... }
通過打包調用API,確保它返回通過用異常類型,從而簡化代碼
// good LocalPort port = new LocalPort(12); try { port.open(); } catch(PortDeviceFailure e) { reportError(e); logger.log(e.getMessage(),e); } finally { // ..... } public class LocalPort{ private ACMEPort innerPort; public LocalPort(int portNumber){ innerPort = new ACMEPort(portNumber); } public open() { try { innerPort.open(); } catch(DeviceResponseException e) { // 自定義的異常類 throw new PortDeviceFailure(e); } catch(ATM1212UnlockedException e) { throw new PortDeviceFailure(e); } catch(GMXError e) { throw new PortDeviceFailure(e); } } }
將第三方API打包是個良好的實踐手段。當你打包一個第三方API,你就降低了對它的依賴。
其他try catch語句塊的范圍不要太大,這樣不利于對異常的分析
別返回null值,這樣可以減少調用者的防御性檢測。與其返回null,不如拋出異常,或是返回特例對象(特例對象詳見 p101)
別傳遞null值,傳遞null就要求被調用函數需要一系列防御性檢測,也就意味著程序有更大可能出錯
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/76718.html
摘要:看完代碼整潔之道之后我受益匪淺,但等到自己實踐時卻很難按照書中給的建議編寫出整潔的代碼。意味著新人除了了解代碼邏輯之外,還需要學習這種編碼語言。代碼在演化,注釋卻不總是隨之變動。區隔與靠近空格強調左右兩邊的分割。 看完《代碼整潔之道》之后我受益匪淺,但等到自己實踐時卻很難按照書中給的建議編寫出整潔的代碼。一方面是規則太多,記不住,另一方面書上引用了大量示例代碼對這些規則進行佐證,在我記...
摘要:在代碼整潔之道,提出一種軟件質量,可持續開發不僅在于項目架構設計,還與代碼質量密切相關,代碼的整潔度和質量成正比,一份整潔的代碼在質量上是可靠的,為團隊開發,后期維護,重構奠定了良好的基礎。 現在的軟件系統開發難度主要在于其復雜度和規模,客戶需求也不再像Winston Royce瀑布模型期望那樣在系統編碼前完成所有的設計滿足用戶軟件需求。在這個信息爆炸技術日新月異的時代,需求總是在不停...
摘要:我們這里再介紹一下,朱重八家族的名字,都很有特點。取這樣的名字不是因為朱家是搞數學的,而是因為在元朝,老百姓如果不能上學和當官就沒有名字,只能以父母年齡相加或者出生的日期命名。所以說命名不僅僅是一種科學,更是一種藝術。 在小朱元璋出生一個月后,父母為他取了一個名字(元時慣例):朱重八,這個名字也可以叫做朱八八。我們這里再介紹一下,朱重八家族的名字,都很有特點。朱重八高祖名字:朱百六;朱...
閱讀 3128·2021-09-22 15:50
閱讀 3330·2021-09-10 10:51
閱讀 3142·2019-08-29 17:10
閱讀 2918·2019-08-26 12:14
閱讀 1835·2019-08-26 12:00
閱讀 931·2019-08-26 11:44
閱讀 652·2019-08-26 11:44
閱讀 2816·2019-08-26 11:41