編寫(xiě)可讀代碼的藝術(shù)

編寫(xiě)可讀代碼的藝術(shù)

書(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ū)里文字的引用與整理


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末认臊,一起剝皮案震驚了整個(gè)濱河市圃庭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌失晴,老刑警劉巖剧腻,帶你破解...
    沈念sama閱讀 212,542評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異涂屁,居然都是意外死亡书在,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)胯陋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)蕊温,“玉大人袱箱,你說(shuō)我怎么就攤上這事∫迕” “怎么了发笔?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,021評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)凉翻。 經(jīng)常有香客問(wèn)我了讨,道長(zhǎng),這世上最難降的妖魔是什么制轰? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,682評(píng)論 1 284
  • 正文 為了忘掉前任前计,我火速辦了婚禮,結(jié)果婚禮上垃杖,老公的妹妹穿的比我還像新娘男杈。我一直安慰自己,他們只是感情好调俘,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,792評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布伶棒。 她就那樣靜靜地躺著,像睡著了一般彩库。 火紅的嫁衣襯著肌膚如雪肤无。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,985評(píng)論 1 291
  • 那天骇钦,我揣著相機(jī)與錄音宛渐,去河邊找鬼。 笑死眯搭,一個(gè)胖子當(dāng)著我的面吹牛窥翩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播鳞仙,決...
    沈念sama閱讀 39,107評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼鳍烁,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了繁扎?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,845評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤糊闽,失蹤者是張志新(化名)和其女友劉穎梳玫,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體右犹,經(jīng)...
    沈念sama閱讀 44,299評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡提澎,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,612評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了念链。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片盼忌。...
    茶點(diǎn)故事閱讀 38,747評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡积糯,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出谦纱,到底是詐尸還是另有隱情看成,我是刑警寧澤,帶...
    沈念sama閱讀 34,441評(píng)論 4 333
  • 正文 年R本政府宣布跨嘉,位于F島的核電站川慌,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏祠乃。R本人自食惡果不足惜梦重,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,072評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望亮瓷。 院中可真熱鬧琴拧,春花似錦、人聲如沸嘱支。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,828評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)斗塘。三九已至赢织,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間馍盟,已是汗流浹背于置。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,069評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留贞岭,地道東北人八毯。 一個(gè)月前我還...
    沈念sama閱讀 46,545評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像瞄桨,于是被迫代替她去往敵國(guó)和親话速。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,658評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容