摘要:但大多數(shù)情況下應(yīng)該盡量利用一些機(jī)制將二元函數(shù)轉(zhuǎn)換成一元函數(shù)。應(yīng)該為起一個(gè)更能描述函數(shù)功能的函數(shù)名副作用在于對(duì)這個(gè)調(diào)用函數(shù),顧名思義,就是用來檢查密碼。注釋及其描述的代碼之間的聯(lián)系應(yīng)該顯而易見。受控異常的代價(jià)就是違反開放閉合原則。
大師級(jí)的程序員把系統(tǒng)當(dāng)作故事來講,而不是當(dāng)作程序來寫。有意義的命名 做有意義的區(qū)分
如果同一作用范圍內(nèi)兩樣不同的東西不能重名,那其意思也應(yīng)該不同才對(duì)。那么這兩樣?xùn)|西應(yīng)該取不同的名字而不是以數(shù)字區(qū)分。如果以下代碼參數(shù)名改為 source 和 destination,這個(gè)函數(shù)就會(huì)像樣許多
public static void copyChars(char a1[], char a2[]){ for(int i = 0; i < a1.length; i++){ a2[i] = a1[i]; } }
以數(shù)字系列命名(a1、a2、a3...)是依意義命名的對(duì)立面。這樣的名稱完全沒有提供正確的信息
Info和Data就像a、an和the一樣,是意義含糊的廢話。但有時(shí)候只要體現(xiàn)出有意義的區(qū)分,使用a和the這樣的前綴就沒錯(cuò)
廢話都是冗余的。Variable一詞永遠(yuǎn)不應(yīng)當(dāng)出現(xiàn)在變量名中。Table一詞永遠(yuǎn)不應(yīng)該出現(xiàn)在表名中
避免思維映射例如循環(huán)中的 i、j、k 等單字母名稱不是個(gè)好選擇;讀者必須在腦中將它映射為真實(shí)概念。最好用 filter、map 等方法代替 for循環(huán)
類名與方法名類名和對(duì)象名應(yīng)該是名稱或名詞短語
方法名應(yīng)該是動(dòng)詞或動(dòng)詞短語
每個(gè)概念對(duì)應(yīng)一個(gè)詞例如 fetch、retrive、 get 表達(dá)同一個(gè)意思,應(yīng)該選定一個(gè),然后在各個(gè)類中使用相同的方法名。
別用雙關(guān)語避免將同一單詞用于不同的目的。同一術(shù)語用于不同概念,基本上就是雙關(guān)語了。
使用解決方案領(lǐng)域名稱記住,只有程序員才會(huì)讀你的代碼。所以,盡管用那些計(jì)算機(jī)科學(xué)(Computer Science,CS)術(shù)語、算法名、模式名等。
動(dòng)詞與關(guān)鍵字給函數(shù)取個(gè)好名字,能較好地解釋函數(shù)的意圖,以及參數(shù)的順序和意圖。
對(duì)于一元函數(shù),函數(shù)和參數(shù)應(yīng)當(dāng)形成一種非常良好的動(dòng)詞/名詞形式。
// good write(name) // better // 更具體,它告訴我們,"name"是一個(gè)"field" writeField(name)
函數(shù)名稱的關(guān)鍵字(keyword)形式。使用這種形式,把參數(shù)的名稱編碼成了函數(shù)名
// bad assertEqual(expected, actual); // good // 這大大減輕了記憶參數(shù)順序的負(fù)擔(dān) assertExpectedEqualsActual(expected, actual);函數(shù) 短小
函數(shù)第一條規(guī)則是要短小。第二條規(guī)則不是要短小。越短小越好,20行封頂
if、else、while等語句,其中的代碼應(yīng)該只有一行。該行大抵應(yīng)該是一個(gè)函數(shù)調(diào)用語句。因?yàn)閴K內(nèi)的函數(shù)擁有較具體說明性的名稱,從而增加了文檔上的價(jià)值
只做一件事確保函數(shù)不能被再拆分
參數(shù)最理想的參數(shù)數(shù)量是零,其次是一,再次是二,應(yīng)盡量避免三
不要傳遞標(biāo)識(shí)參數(shù),標(biāo)識(shí)參數(shù)大聲宣布函數(shù)不是做一件事。如果標(biāo)識(shí)為 true 將會(huì)這樣,標(biāo)識(shí)為 false 則會(huì)那樣
二元函數(shù):有些時(shí)候兩個(gè)參數(shù)正好。例如 Point p = new Point(0, 0);因?yàn)辄c(diǎn)天生擁有兩個(gè)參數(shù)。但大多數(shù)情況下應(yīng)該盡量利用一些機(jī)制將二元函數(shù)轉(zhuǎn)換成一元函數(shù)。例如,把writeField 方法寫成outputStream的成員之一
// bad writeField(outputStream, name); // good outputStream.writeFiled(name);
參數(shù)對(duì)象:如果函數(shù)看起來需要兩個(gè)、三個(gè)、或三個(gè)以上參數(shù),就說明其中一些應(yīng)該封裝為類了
// bad Circle makeCircle(double x, double y, double, radius); // good Circle makeCircle(Point center, double radius);
從參數(shù)封裝成對(duì)象,從而減少參數(shù)數(shù)量,看起來像是在作弊,但實(shí)則并非如此。當(dāng)一組參數(shù)被共同傳遞,就像上例中的x和y那樣,往往就是該有自己名稱的某個(gè)概念的一部分
無副作用
確保函數(shù)功能就像函數(shù)名描述的一樣,不要做函數(shù)名描述以外的事情。應(yīng)該為起一個(gè)更能描述函數(shù)功能的函數(shù)名
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)) { // 副作用在于對(duì)這個(gè)調(diào)用 // checkPassword函數(shù),顧名思義,就是用來檢查密碼。該名稱并未暗示它會(huì) // 初始化該次會(huì)話??梢灾孛麨?checkPasswordAndIntializeSession Session.initialize(); return true; } } return false; } }
普通而言,應(yīng)該避免使用輸出參數(shù),如果函數(shù)必須要修改某種狀態(tài),就修改所屬對(duì)象的狀態(tài)
// bad // 讀者會(huì)弄不清s是輸入?yún)?shù)還是輸出參數(shù) // 也會(huì)弄不清這函數(shù)是把s添加到什么東西后面,還是把什么東西添加到s后面 appendFooter(s); // 函數(shù)簽名 public void appendFooter(StringBuffer report){} // good report.appendFooter();分隔指令與詢問
函數(shù)要行做什么事( 例如 user.setName("xxx") )、要么回答什么事( 例如 user.isVip() )。一個(gè)函數(shù)里不要把兩件事都干了。
如何寫出好函數(shù)分解函數(shù)
修改名稱
消除重復(fù)
注釋 好的注釋法律信息
警示性注釋
TODO 注釋雖好,但也要定期查看,刪除不再需要的
壞的注釋循規(guī)式注釋。 例如每個(gè)函數(shù)都要有Javadoc或每個(gè)變量都要有注釋的規(guī)矩全然是愚蠢可笑的。這類注釋徒然讓代碼變得散亂
注釋掉的代碼。 現(xiàn)在已經(jīng)有源代碼控制系統(tǒng),不要的代碼應(yīng)該立即刪掉
不明顯的聯(lián)系。 注釋及其描述的代碼之間的聯(lián)系應(yīng)該顯而易見。注釋的作用是解釋未能自行解釋的代碼。如果注釋本身還需要解釋,就太遺憾了
切斷代碼間的聯(lián)系
// 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); } }
變量聲名。 大多數(shù)情況下變量聲名應(yīng)該盡可能靠近其使用的位置。但是在類內(nèi),變量聲名應(yīng)該統(tǒng)一放在頂部,因?yàn)檫@樣讀者可以一眼看出這個(gè)類有什么變量。
相關(guān)函數(shù)。 若某個(gè)函數(shù)調(diào)用了另一個(gè)函數(shù),就應(yīng)該把它們放到一起,而且調(diào)用者應(yīng)該盡可能放在被調(diào)用者上面。這樣程序就有個(gè)自然順序。
概念相關(guān)。 概念相關(guān)的代碼應(yīng)該放到一起。相關(guān)性越強(qiáng),彼此之間的距離就該越短
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(){} // ..... }
這些函數(shù)有關(guān)極強(qiáng)的概念相關(guān)性,因?yàn)樗麄儞碛泄餐拿J剑瑘?zhí)行同一基礎(chǔ)任務(wù)的不同變種。互相調(diào)用是第二位的。即便沒有互相調(diào)用。也應(yīng)該放在一起。
更多例子查看 p79 -- 5.25 垂直順序
錯(cuò)誤處理 抽離Try/Catch代碼塊函數(shù)應(yīng)該只做一件事,錯(cuò)誤處理就是一件事。
// 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{ // 將上例的操作,封裝到一個(gè)方法 deletePageAndAllReferences(page); }catch(Expection e){ logError(e); } }使用非受控異常
受控異常:Checked Exception(FileException、SQLException等),這類異常必須寫 try/catch,或者 throw拋出,否則編譯通不過。
非受控異常:Unchecked Exception,這類異常也叫做運(yùn)行時(shí)異常(與非受控異常 字?jǐn)?shù)相等),這類異常不需要 try/catch,也不需要 throw拋出。即使 throw 了,上層調(diào)用函數(shù)也非必須捕獲,編譯能通過。
受控異常的代價(jià)就是違反開放/閉合原則。如果你在方法中拋出受控異常,這意味著每個(gè)調(diào)用該函數(shù)的函數(shù)都要修改,捕獲新異常,或在其簽名中添加合適的throw語句。對(duì)于一般的應(yīng)用開發(fā),受控異常依賴成本要高于收益成本,盡量 try/catch 處理,不要拋出。
給出異常發(fā)生的環(huán)境說明應(yīng)創(chuàng)建信息充分的錯(cuò)誤信息,并和異常一起傳遞出去。在消息中,包括失敗的操作和失敗類型。如果你的應(yīng)用程序有日志系統(tǒng),傳遞足夠的信息給catch塊,并記錄下來。
依調(diào)用者需要定義異常類// 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 { // ..... }
通過打包調(diào)用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打包是個(gè)良好的實(shí)踐手段。當(dāng)你打包一個(gè)第三方API,你就降低了對(duì)它的依賴。
其他try catch語句塊的范圍不要太大,這樣不利于對(duì)異常的分析
別返回null值,這樣可以減少調(diào)用者的防御性檢測。與其返回null,不如拋出異常,或是返回特例對(duì)象(特例對(duì)象詳見 p101)
別傳遞null值,傳遞null就要求被調(diào)用函數(shù)需要一系列防御性檢測,也就意味著程序有更大可能出錯(cuò)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/76718.html
摘要:看完代碼整潔之道之后我受益匪淺,但等到自己實(shí)踐時(shí)卻很難按照書中給的建議編寫出整潔的代碼。意味著新人除了了解代碼邏輯之外,還需要學(xué)習(xí)這種編碼語言。代碼在演化,注釋卻不總是隨之變動(dòng)。區(qū)隔與靠近空格強(qiáng)調(diào)左右兩邊的分割。 看完《代碼整潔之道》之后我受益匪淺,但等到自己實(shí)踐時(shí)卻很難按照書中給的建議編寫出整潔的代碼。一方面是規(guī)則太多,記不住,另一方面書上引用了大量示例代碼對(duì)這些規(guī)則進(jìn)行佐證,在我記...
摘要:在代碼整潔之道,提出一種軟件質(zhì)量,可持續(xù)開發(fā)不僅在于項(xiàng)目架構(gòu)設(shè)計(jì),還與代碼質(zhì)量密切相關(guān),代碼的整潔度和質(zhì)量成正比,一份整潔的代碼在質(zhì)量上是可靠的,為團(tuán)隊(duì)開發(fā),后期維護(hù),重構(gòu)奠定了良好的基礎(chǔ)。 現(xiàn)在的軟件系統(tǒng)開發(fā)難度主要在于其復(fù)雜度和規(guī)模,客戶需求也不再像Winston Royce瀑布模型期望那樣在系統(tǒng)編碼前完成所有的設(shè)計(jì)滿足用戶軟件需求。在這個(gè)信息爆炸技術(shù)日新月異的時(shí)代,需求總是在不停...
摘要:我們這里再介紹一下,朱重八家族的名字,都很有特點(diǎn)。取這樣的名字不是因?yàn)橹旒沂歉銛?shù)學(xué)的,而是因?yàn)樵谠?,老百姓如果不能上學(xué)和當(dāng)官就沒有名字,只能以父母年齡相加或者出生的日期命名。所以說命名不僅僅是一種科學(xué),更是一種藝術(shù)。 在小朱元璋出生一個(gè)月后,父母為他取了一個(gè)名字(元時(shí)慣例):朱重八,這個(gè)名字也可以叫做朱八八。我們這里再介紹一下,朱重八家族的名字,都很有特點(diǎn)。朱重八高祖名字:朱百六;朱...
閱讀 3138·2021-09-22 15:50
閱讀 3338·2021-09-10 10:51
閱讀 3153·2019-08-29 17:10
閱讀 2928·2019-08-26 12:14
閱讀 1845·2019-08-26 12:00
閱讀 959·2019-08-26 11:44
閱讀 659·2019-08-26 11:44
閱讀 2829·2019-08-26 11:41