<讀書筆記> 代碼整潔之道
二、關于函數(shù)的一些規(guī)則
本文引用地址:http://m.butianyuan.cn/article/201608/294841.htm1、短小
函數(shù)的第一規(guī)則就是短小。
代碼長度以20行封頂為最佳。
if,else,while語句的代碼塊應該只有一行,這樣不但能保持函數(shù)短小,還能增加可讀性。
函數(shù)的層級不應該多于兩層,這樣才容易理解和閱讀。
2、只做一件事
函數(shù)應該只做一件事,做好這一件事。
判斷函數(shù)是否只做了一件事的方法就是看其是否能夠拆出一個函數(shù),該函數(shù)不是單純的重新詮釋其實現(xiàn)。
3、每個函數(shù)一個抽象層級
要確保函數(shù)做做一件事情,函數(shù)中的語句都要在同一抽象層級上。這是保持函數(shù)短小的要訣。
4、函數(shù)參數(shù)
最好少于3個,0個和1個最佳,3個以上請使用結構體代替。
輸出參數(shù)比輸入參數(shù)更加難以理解,讀函數(shù)時,習慣于認為信息通過參數(shù)輸入函數(shù),通過返回值輸出。
給函數(shù)起個好名字,使其能夠很好的解釋函數(shù)的意圖、以及參數(shù)的順序。比如assertExpectedEqualsActual(expected,actual)就容易理解和記憶。
5、無副作用
副作用指的是被其隱藏起來的事情,比如開機檢查密碼函數(shù),如果調用前密碼未經初始化,就會出錯,就隱藏了其時序性耦合的要求。應該在其函數(shù)中增加初始化密碼的功能,盡管違反了只做一件事的。
6、分割指令與查詢
函數(shù)修改某對象的狀態(tài)或者獲取該對象的參數(shù),應該分為兩個函數(shù)實現(xiàn)。
比如函數(shù)設置某個指定屬性,如果成功就返回true,失敗返回false,用例如下:
if(set("username","unclebob"))...
這樣的語句理解上面有歧義,set是動詞,但是在上下文中感覺像形容詞,從讀者角度看,這句語句的意思是問username的屬性是否已經設置為unclebob?還是詢問username的屬性是否成功設置為unclebob?因此最好改為如下所示:
if(attributeExists("username"))
{
setAttribute("username","unclebob");
}
7、使用異常代替返回錯誤值
直接返回錯誤代碼鼓勵了在if語句中把指令當做表達式使用,會導致多層次嵌套結構。如果返回異常代替錯誤碼,錯誤處理代碼就可以從主路徑代碼中分離出來,得到簡化
8、不要重復
重復的代碼可維護性差,如果算法修改,所涉及的地方都要更新,代碼臃腫,還會增加放過錯誤的可能!
如果一個算法在不同函數(shù)中重復出現(xiàn),則需要使用一個獨立的函數(shù)修復它。
面向對象和面向組件編程也多多少少都是消除重復的策略。
9、結構化編程
只要保持函數(shù)短小,偶爾出現(xiàn)return、break、continue語句沒有什么壞處,甚至比單入單出更具表達力。
goto語句只在大函數(shù)中才能用到,因此盡量避免使用。
10、如何寫出短小的函數(shù)
寫代碼和寫文章一樣,先寫出初稿、再細細打磨,直至達到心中的樣子。
初寫的函數(shù),一般都是冗長而復雜,有太多縮進和嵌套循環(huán),名稱隨意性大,代碼有重復。通過分解函數(shù)、修改名稱、消除重復,然后遵循本章的規(guī)則,重新組裝函數(shù),就可以寫出短小精煉的函數(shù)。
三 關于注釋的規(guī)則
1、注釋不能美化糟糕的代碼
帶有少量注釋的整潔而有表達力的代碼,要比帶有大量注釋的零碎而復雜的代碼像樣的多!
與其花時間寫注釋,不如花時間清理早搞定糟糕的代碼。
代碼在變動、演化,而很不幸,注釋不是總隨之變動,時間越久,注釋離代碼的本意就越久。只有代碼是唯一真正準確的信息來源。。
2、用代碼闡述
使用代碼本身解釋其行為。
比如//Check to see if the employee is eligible for full benefits
if((employee.flags &HOURLY_FLAG)&&(employee.age > 65))
通過創(chuàng)建與注釋所言同一事物的函數(shù)即可。
if(employee.isEligibleForFullBenefits())
3、好注釋
法律信息,比如公司代碼規(guī)范要求寫的有關法律的注釋,版權聲明等。
提供信息的注釋,更好的方法是使用函數(shù)名稱傳達信息。
對意圖的解釋:注釋不僅提供了有關實現(xiàn)的有用信息,而且還提供了某個決定后面的意圖。
闡釋:把晦澀難懂的參數(shù)或者返回值翻譯為可讀形式的注釋是有用的。
警示:用于警告其它程序員會出現(xiàn)某種后果的注釋也是有用的。
4、壞的注釋
喃喃自語式:只是覺得因為過程需要就增加的注釋。
多余的注釋:本身并不能比代碼提供更多的信息,就是多余。
誤導性注釋:含義不精確或者容易引起誤導的注釋。
循規(guī)性注釋:每個函數(shù)或者每個變量都要有注釋的規(guī)矩是全然可笑的,只會搞亂代碼。
日志式注釋:在有源碼控制系統(tǒng)的今天,這種冗長的記錄只會讓模塊變得凌亂不堪,應該刪除
廢話注釋:對顯然之事喋喋不休,毫無新意??创a的人員,也幾乎對其視而不見。因此用整理代碼的決心替代廢話的沖動,會使你成為更優(yōu)秀的程序員。
可怕的廢話:在知名開源庫的J**adoc中,也可以看到復制黏貼錯誤,意思就是變量不一樣,注釋卻一樣。
位置標記:少用標記欄,比如//Action//////////////////////////////,特定函數(shù)放在這個下面,多數(shù)時候是在不必要。
括號后面的注釋:盡管對于多層嵌套有幫助,但是更應該使用短小、封裝的函數(shù),如果你想標記右括號,其實應該做的是縮短函數(shù)。
歸屬與署名:源碼控制系統(tǒng)很善于記錄是誰在何時改動了什么,沒有必要用小小的簽名搞臟代碼。比如注釋/*Added by Rick*/
注釋掉的代碼:直接把代碼注釋掉是討厭的做法,別人可能認為其一定有原因才留在這里不敢刪除,最后注釋掉的代碼像渣滓一樣堆在那里,別這么干!
信息過多的注釋:別再注釋中增加歷史性話題或者無關的細節(jié)描述。
不明顯的聯(lián)系:注釋的作用是解釋未能自我解釋的代碼,如果本身還需要解釋就沒有必要了。
函數(shù)頭:短函數(shù)不需要太多注釋,只需要寫一個好名字就比注釋強得多!
四 關于代碼格式的規(guī)則
你應該保持良好的代碼格式,選用一套管理代碼格式的簡單規(guī)則,然后貫徹實施。如果在團隊中工作,則團隊應該一致同意采用一套簡單的格式規(guī)則,所有成員都要遵從。使用能幫你應用這些格式規(guī)則的自動化工具會很有幫助。
1、格式的目的
原始代碼修改很久之后,其代碼風格和可讀性仍會影響到可維護性和擴展性。
代碼格式關乎溝通,而溝通時專業(yè)開發(fā)者的頭等大事!
哪些代碼格式方面能幫助我們溝通呢?
2、垂直格式
垂直尺寸
單個文件的長度尺寸與可理解性相關,統(tǒng)計數(shù)據表明,用大多數(shù)為200行到500行的單個文件可以構造出出色的系統(tǒng),短文件通常比長文件容易理解!
函數(shù)布局
函數(shù)名稱應該足夠告訴我們是否在正確的模塊中,源文件頂部應該給出高層次的概念和算法,細節(jié)應該向下逐次展開,直至源文件中最低層的函數(shù)和細節(jié)。就像報紙一樣,標題統(tǒng)領提綱概要,內容逐次展開細節(jié)。
空行
在包聲明、函數(shù)之間、代碼塊之間應該使用空行隔開,這條簡單的規(guī)則極大的影響到代碼的視覺外觀??瞻仔凶鳛橐粭l線索,標示出獨立的概念。
靠近 如果說空白行隔開了概念,靠近的代碼則暗示了他們之間的緊密聯(lián)系。因此關系緊密的應該互相靠近.
垂直距離
全局變量聲明應該放到文件第一個函數(shù)聲明前,局部變量聲明放到函數(shù)頂部。相關函數(shù)應該放在一起,調用者放在被調用者上面,這樣就能輕易找到被調用函數(shù),極大增強模塊的可讀性
3、橫向格式
行寬
一行代碼應該多寬?統(tǒng)計數(shù)據表明,70%的代碼行少于60個字符,代碼行應該盡量短小,死守80字節(jié)有點僵化,但是不要超過100字符或者120字符。簡單的規(guī)則是無需向右拖動滾動條,就可以看到全部代碼。
水平區(qū)隔與靠近
在運算符號兩端(乘號除外,因其優(yōu)先級較高,多數(shù)格式化工具都忽視優(yōu)先級)、函數(shù)名和右括號之間之間,函數(shù)參數(shù)之間增加空格!
水平對齊
經驗表明一組類聲明中的變量名、或者賦值語句的右值對齊沒有什么實際作用,因此左端對齊,使用同樣的空格區(qū)分規(guī)則即可。
縮進
有縮進的代碼結構,閱讀時可以很容易查找新的聲明、變量、類和函數(shù)塊,否則就需要折騰一番才能明白,程序員應該依賴縮進模式(在python中強制使用縮進表示for循環(huán)的范圍,C語言中使用大括號,但是也要依賴縮進增強可讀性)。
空范圍
有時while或for語句的語句體為空,如下所示.右端的分號很難看見,容易讓人迷惑。
while(dis.read(buf,0,readBufferSize) != 1);
因此最好改為如下格式
while(dis.read(buf,0,readBufferSize) != 1)
{
}
團隊規(guī)則
在團隊中,各成員應該采用一致的代碼格式,如果各自風格不同,會增加項目代碼的復雜度。
五 單元測試
過去10年來,編程專業(yè)領域進步很大,特別是敏捷和TDD(測試驅動開發(fā))運動鼓舞了很多程序員編寫單元測試。TDD要我們在編寫生產代碼之前先編寫測試,其還有三大定律:
定律一:再編寫不能測試通過的測試驅動前,不可編寫生產代碼
定律二:只可編寫剛好無法通過的單元測試,不能編譯也不算通過
定律三:只可編寫剛好足以通過當前失敗測試的生產代碼。
這些定律要求測試與生產代碼一起編寫,測試代碼先寫,這樣寫程序,測試將覆蓋所有生產代碼。
1、保持測試的整潔
測試代碼和生產代碼一樣,需要被思考、設計和照料,像生產代碼一樣整潔
正是單元測試讓你的代碼可擴展、可維護、可復用。如果沒有測試,每次修改都可能帶來缺陷,無論架構如何有擴展性,設計劃分如何好,如果沒有單元測試,你的改動都可能帶來不可預知的缺陷。
2、整潔的測試
整潔的唯一要素的就是可讀性,在單元測試中,可讀性比生產代碼還重要。如何做到可讀性?就是要明確、簡潔和具有足夠的表達力。
測試要采用構造-操作-檢驗模式,每個測試均要可以清晰的拆分為三個環(huán)節(jié),第一個環(huán)節(jié)構造測試數(shù)據,第二個環(huán)節(jié)操作測試數(shù)據,第三個環(huán)節(jié)檢驗操作是否得到期望的結果。
3、每個測試一個斷言
單個測試函數(shù)有且僅有一個斷言是個好準則。
每個測試做測試一個概念,如果一個測試測量三件事情,可能會導致遺漏。
4、整潔的5條規(guī)則
快速(fast),測試應該快速運行,你才能想要頻繁運行它,如果不頻繁運行,就不能盡早發(fā)現(xiàn)問題。
獨立(Independent),測試應該互相獨立,從而可以單獨運行每個測試。
可重復(Repeatable),測試應該在任何環(huán)境中重復通過,否則當前環(huán)境具備時,你也會無法運行測試。
自足驗證(Self-Validating),測試應該有布爾值輸出去,無論如何不應該查看日志文件來確認測試是否通過。
及時(Timely),測試應該及時編寫,如果在生產代碼之后編寫測試,你會發(fā)現(xiàn)代碼難以測試。
評論