書(shū)名:編寫(xiě)可讀代碼的藝術(shù)
作者:Dustin Boswell & Trevor Foucher
譯者:尹哲摹恨,鄭秀雯
以下是書(shū)里文字的引用與整理
前言
可讀性基本定理:代碼的寫(xiě)法應(yīng)當(dāng)使別人理解它所需的時(shí)間最小化贩幻。
一条舔、表面層次的改進(jìn)
1. 把信息裝到名字里
1)選擇專業(yè)的詞
清晰和精確比裝可愛(ài)好堆巧。
單詞 | 更多選擇 |
---|---|
Get | Fetch, Download |
Stop | Kill, Pause |
Send | Deliver, Dispatch, Announce, Distribute, Route |
Find | Search, Extract, Locate, Recover |
Start | Launch, Create, Begin, Open |
Make | Create, SetUp, Build, Generate, Compose, Add, New |
2)避免使用像tmp和retval這樣泛泛的名字
retval這個(gè)名字沒(méi)有包含很多信息业踢,用一個(gè)描述該變量的值的名字來(lái)代替它拭荤。
tmp這個(gè)名字只應(yīng)用于短期存在且臨時(shí)性為其主要存在因素的變量。
如果你要使用像tmp然痊、it或者retval這樣空泛的名字革骨,那么你要有個(gè)好的理由农尖。
3)用具體的名字代替抽象的名字
在給變量、函數(shù)或者其他元素命名時(shí)良哲,要把它描述得更具體而不是更抽象盛卡。
4)為名字附帶更多信息
如果你的變量是一個(gè)度量的話(如時(shí)間長(zhǎng)度或者字節(jié)數(shù)),那么最好把名字帶上它的單位筑凫。
函數(shù)參數(shù) | 帶單位的參數(shù) |
---|---|
Start(int delay) | delay -> delay_secs |
CreateCache(int size) | size -> size_mb |
ThrottleDownload(float limit) | limit -> max_kbps |
Rotate(float angle) | angle -> degrees_cw |
在對(duì)于這個(gè)變量存在危險(xiǎn)或者意外的任何時(shí)候你也該采用它滑沧。
情形 | 變量名 | 更好的名字 |
---|---|---|
一個(gè)“純文本”格式的密碼,需要加密后才能進(jìn)一步使用 | password | plaintext_password |
一條用戶提供的注釋巍实,需要轉(zhuǎn)義之后才能用于顯示 | comment | unescaped_comment |
已轉(zhuǎn)化為UTF-8格式的html字節(jié) | html | html_utf8 |
以“url方式編碼”的輸入數(shù)據(jù) | data | data_urlenc |
5)名字應(yīng)該有多長(zhǎng)
在小的作用域里可以使用短的名字滓技,如果一個(gè)標(biāo)識(shí)符有較大的作用域,那么它的名字就要包含足夠的信息以便含義更清楚棚潦。
對(duì)于首字母縮略詞和縮寫(xiě)令漂,使用項(xiàng)目所特有的縮寫(xiě)詞非常糟糕,經(jīng)驗(yàn)原則是:團(tuán)隊(duì)的新成員是否能理解這個(gè)名字的含義瓦盛。
丟掉沒(méi)用的詞:有時(shí)名字中的某些單詞可以拿掉而不會(huì)損失任何信息洗显。
6)利用名字的格式來(lái)傳遞含義
項(xiàng)目中使用一致的命名規(guī)范,使得對(duì)于下劃線原环、連字符和大小寫(xiě)的使用方式也可以把更多的信息裝到名字中挠唆。
2. 不會(huì)誤解的名字
- 要多問(wèn)自己幾遍:“這個(gè)名字會(huì)被別人解讀成其他的意義嗎?”
- 與使用者的期望相匹配
- 用min和max來(lái)表示(包含)極限
- 用first和last來(lái)表示包含的范圍
- 用begin和end來(lái)表示包含/排除范圍
- 使用is和has這樣的詞來(lái)明確表示它是布爾值嘱吗,避免使用反義的詞
3. 審美
1)三條原則
- 使用一致的布局玄组,讓讀者很快就習(xí)慣這種風(fēng)格
- 讓相似的代碼看上去相似
- 把相關(guān)的代碼行分組滔驾,形成代碼塊
2)方法
- 重新安排換行來(lái)保持一致和緊湊
- 用方法來(lái)整理不規(guī)則的東西
- 在需要時(shí)使用列對(duì)齊
- 選一個(gè)有意義的順序,始終一致的使用它:
- 讓變量的順序與對(duì)應(yīng)的HTML表單中<input>字段的順序相匹配
- 從“最重要”到“最不重要”排序
- 按字母順序排序
- 把聲明按塊組織起來(lái)
- 把代碼分成“段落”
- 一致的風(fēng)格比“正確”的風(fēng)格更重要
4. 該寫(xiě)什么樣的注釋
注釋的目的是盡量幫助讀者了解得和作者一樣多俄讹。
1)什么不需要注釋
- 不要為那些從代碼本身就能快速推斷的事實(shí)寫(xiě)注釋
- 不要為了注釋而注釋
- 不要給不好的名字加注釋——應(yīng)該把名字改好(好代碼 > 壞代碼 + 好注釋)
2)記錄你的思想
- 記錄你對(duì)代碼有價(jià)值的見(jiàn)解
- 為代碼中的瑕疵寫(xiě)注釋
- 給常量加注釋
3)站在讀者的角度
- 回答意料之中的提問(wèn)
- 公布可能的陷阱
- “全局觀”注釋:類(lèi)之間如何交互哆致、數(shù)據(jù)如何在整個(gè)系統(tǒng)中流動(dòng)、以及入口點(diǎn)在哪里
- 總結(jié)性注釋
4)客服“作者心理阻滯”
- 不管你心里想什么患膛,先把它寫(xiě)下來(lái)
- 讀一下這段注釋摊阀,看看有沒(méi)有什么地方可以改進(jìn)
- 不斷改進(jìn)
5. 寫(xiě)出言簡(jiǎn)意賅地注釋
注釋?xiě)?yīng)當(dāng)有很高的“信息/空間率”
- 讓注釋保持緊湊
- 避免使用不明確的代詞
- 潤(rùn)色粗糙的句子
- 精確的描述函數(shù)的行為
- 用輸入/輸出例子來(lái)說(shuō)明特別的情況
- 聲明代碼的意圖
- “具名函數(shù)參數(shù)”的注釋
- 采用信息含量高的詞
二、簡(jiǎn)化循環(huán)和邏輯
1. 把控制流變得易讀
把條件踪蹬、循環(huán)以及其他對(duì)控制流的改變做得越“自然”越好
運(yùn)用一種方式使讀者不用停下來(lái)重讀你的代碼
1)條件語(yǔ)句中參數(shù)的順序
比較的左側(cè) | 比較的右側(cè) |
---|---|
“被詢問(wèn)的”表達(dá)式胞此,它的值更傾向于不斷變化 | 用來(lái)作比較的表達(dá)式,它的值更傾向于常量 |
2)if/else語(yǔ)句塊的順序
- 首先處理正邏輯而不是負(fù)邏輯的情況
- 先處理掉簡(jiǎn)單的情況
- 先處理有趣的或者是可疑的情況
3)?:條件表達(dá)式(三目運(yùn)算符)
默認(rèn)情況下都用if/else跃捣,三目運(yùn)算符?:只有在最簡(jiǎn)單的情況下使用漱牵。
4)避免do/while循環(huán)
do/while的奇怪之處是一個(gè)代碼塊是否會(huì)執(zhí)行時(shí)由其后的一個(gè)條件決定的。通常來(lái)說(shuō)疚漆,邏輯條件應(yīng)該出現(xiàn)在它們所“保護(hù)”的代碼之前酣胀,這就使得很多讀者最后會(huì)讀do/while代碼兩遍。
但僅僅是為了去掉do/while循環(huán)而重復(fù)一段代碼是有點(diǎn)愚蠢的做法:
body
while(condition){
body(again)
}
幸運(yùn)的是娶聘,我們發(fā)現(xiàn)實(shí)踐當(dāng)中大多數(shù)的do/while循環(huán)都可以寫(xiě)成這樣開(kāi)頭的while循環(huán):
public boolean ListHasNode(Node node, String name, int max_length){
while(node != null && max_length > 0){
if(node.name().equals(name)) return true;
node = node.next();
}
return false;
}
5)從函數(shù)中提前返回
有些程序員認(rèn)為函數(shù)中永遠(yuǎn)不應(yīng)該出現(xiàn)多條return語(yǔ)句闻镶,這是胡說(shuō)八道。從函數(shù)中提前返回沒(méi)有問(wèn)題丸升,而且常常很受歡迎儒溉。
想要單一出口點(diǎn)的一個(gè)動(dòng)機(jī)是保證調(diào)用函數(shù)結(jié)尾的清理代碼。但現(xiàn)代的編程語(yǔ)言為這種保證提供了更精細(xì)的方式:
語(yǔ)言 | 清理代碼的結(jié)構(gòu)化術(shù)語(yǔ) |
---|---|
C++ | 析構(gòu)函數(shù) |
Java发钝、Python | try finally |
Python | with |
C# | using |
6)最小化嵌套
- 當(dāng)你對(duì)代碼做改動(dòng)時(shí),從全新的角度審視它波闹,把它作為一個(gè)整體來(lái)看待
- 通過(guò)提早返回來(lái)減少嵌套(if:return; for:continue)
2. 拆分超長(zhǎng)的表達(dá)式
把你的超長(zhǎng)表達(dá)式拆分成更容易理解的小塊
1)用作解釋的變量
拆分表達(dá)式最簡(jiǎn)單的方法就是引入一個(gè)額外的變量酝豪,讓它來(lái)表示一個(gè)小一點(diǎn)的子表達(dá)式:
if line.split(':')[0].strip() == "root";
username = line.split(':')[0].strip();
if username == "root";
2)總結(jié)變量
總結(jié)變量的目的只是用一個(gè)短很多的名字來(lái)代替一大塊代碼,這個(gè)名字會(huì)更容易管理和思考:
原表達(dá)式 | 總結(jié)變量 |
---|---|
if(request.user.id == document.owner_id | user_owns_document |
if(request.user.id != document.owner_id | !user_owns_document |
3)使用德摩根定理
“分別取反精堕,轉(zhuǎn)換與/或”孵淘,有時(shí)你可以使用這個(gè)法則來(lái)讓布爾表達(dá)式更具可讀性。
4)濫用短路邏輯
要小心“智能”的小代碼塊——它們往往在以后會(huì)讓別人讀起來(lái)感到困惑歹篓。
3. 變量與可讀性
1)減少變量
變量越多瘫证,就越難全部跟蹤它們的動(dòng)向
- 沒(méi)有價(jià)值的臨時(shí)變量:沒(méi)有拆分任何復(fù)雜的表達(dá)式、沒(méi)有做更多的澄清庄撮、只用過(guò)一次
- 減少中間結(jié)果:通過(guò)讓代碼提前返回來(lái)處理中間用來(lái)保存臨時(shí)結(jié)果的變量
- 減少控制流變量
2)縮小變量的作用域
讓你的代碼對(duì)盡量少的代碼行可見(jiàn)
- 把大的類(lèi)拆分成小一些的類(lèi)背捌,并且這些類(lèi)之間相互獨(dú)立
- 大文件拆分成小文件,大函數(shù)拆分成小函數(shù)
- “盡量使方法變成靜態(tài)的”洞斯,靜態(tài)方法是讓讀者知道“這幾行代碼與那些變量無(wú)關(guān)”的好辦法
- 把定義向下移毡庆,把每個(gè)定義移到它的使用之前
3)只寫(xiě)一次的變量更好
操作一個(gè)變量的地方越多,越難確定它的當(dāng)前值
三、重新組織代碼
1. 抽取不相關(guān)的子問(wèn)題
積極地發(fā)現(xiàn)并抽取不相關(guān)的子邏輯:
(1)看看某個(gè)函數(shù)或代碼塊么抗,問(wèn)問(wèn)你自己:這段代碼高層次的目標(biāo)是什么毅否?
(2)對(duì)于每一行代碼,問(wèn)一下:它是直接為了目標(biāo)而工作的嗎蝇刀?這段代碼高層次的目標(biāo)是什么螟加?
(3)如果足夠的行數(shù)在解決不相關(guān)的子問(wèn)題,抽取代碼到獨(dú)立的函數(shù)中吞琐。
類(lèi)型:
- 純工具代碼
- 其他多用途代碼
- 通用代碼
- 項(xiàng)目專有的功能
- 簡(jiǎn)化已有接口
- 按需重塑接口
2. 一次只做一件事
同時(shí)在做幾件事的代碼很難理解捆探,應(yīng)該把代碼組織得一次只做一件事情:
(1)列出代碼所做的所有“任務(wù)”,這里的“任務(wù)”沒(méi)有很?chē)?yán)格的定義——它可以小得如“確保這個(gè)對(duì)象有效”顽分,或者含糊得如“遍歷樹(shù)中所有結(jié)點(diǎn)”徐许;
(2)盡量把這件任務(wù)拆分到不同的函數(shù)中,或者至少是代碼中不同的段落中卒蘸。
3. 把想法變成代碼
如果你不能把一件事解釋給你祖母聽(tīng)的話說(shuō)明你還沒(méi)有真正理解它:
(1)想對(duì)著一個(gè)同事一樣用自然語(yǔ)言描述代碼要做什么
(2)注意描述中所使用的關(guān)鍵詞和短語(yǔ)
(3)寫(xiě)出與描述所匹配的代碼
- 清楚地描述邏輯
- 了解函數(shù)庫(kù)是有幫助的
4. 少寫(xiě)代碼
最好讀的代碼就是沒(méi)有代碼雌隅,知道什么時(shí)候不寫(xiě)代碼可能對(duì)于一個(gè)程序員來(lái)講是他所要學(xué)習(xí)的最重要的技巧。
1)質(zhì)疑和拆分你的需求
不是所有的程序都需要運(yùn)行得快缸沃,100%準(zhǔn)確恰起,并且能處理所有的輸入。如果你真的仔細(xì)檢查你的需求趾牧,有時(shí)你可以把它削減成一個(gè)簡(jiǎn)單的問(wèn)題检盼,只需要較少的代碼。
2)保持小代碼庫(kù)
- 創(chuàng)建越多越好的“工具”代碼來(lái)減少重復(fù)代碼
- 減少無(wú)用代碼或沒(méi)有用的功能(你希望你的程序在內(nèi)存耗盡的情況下仍能工作翘单,因此你有很多耍小聰明的邏輯來(lái)試著從內(nèi)存耗盡的情況下恢復(fù)吨枉,但為什么不通過(guò)一句簡(jiǎn)單的提示“系統(tǒng)內(nèi)存不足,抱歉”并刪除所有內(nèi)存不足的代碼呢哄芜?)
- 讓你的項(xiàng)目保持分開(kāi)的子項(xiàng)目狀態(tài)
3)熟悉你周邊的庫(kù)
每隔一段時(shí)間貌亭,花15分鐘來(lái)閱讀標(biāo)準(zhǔn)庫(kù)中的所有函數(shù)/模塊/類(lèi)型的名字。
以上是書(shū)里文字的引用與整理