讀書筆記---《編寫可讀代碼的藝術》

前言

我們曾經在非常成功的軟件公司中和出色的工程師一起工作,然而我們所遇到的代碼仍有很大的改進空間。實際上,我們曾見到一些很難看的代碼,你可能也見過。但是當我們看到寫得很漂亮的代碼時,會很受啓發。好代碼會很明確告訴你它在做什麼。使用它會很有趣,並且會鼓勵你把自己的代碼寫得更好。本書旨在幫助你把代碼寫得更好。

每一章都會深入編程的某個方面來討論如何使代碼更容易理解。本書分成四部分:表面層次上的改進命名、註釋以及審美——可以用於代碼庫每一行的小提示。簡化循環和邏輯在程序中定義循環、邏輯和變量,從而使得代碼更容易理解。重新組織你的代碼在更高層次上組織大的代碼塊以及在功能層次上解決問題的方法。

第1章 代碼應當易於理解

  • 代碼的寫法應當使別人理解它所需的時間最小化。
  • 減少代碼行數是一個好目標,但把理解代碼所需的時間最小化是一個更好的目標。
  • 經常想一想其他人是不是會覺得你的代碼容易理解

第一部分 表面層次的改進

  • 選擇好的名字
  • 寫好的註釋
  • 把代碼整潔地寫成更好的格式

第2章 把信息裝到名字裏

  • 把信息裝進名字中
  • 選擇專業的詞

    • 避免使用tmp、retval、it、foo泛泛的詞
    • 如果不把循環索引命名爲(i、j、k),另一個選擇可以是(club_i、members_i、user_i)或者,更簡化一點(ci、mi、ui)
  • 用具體的名字替代抽象的名字
  • 使用前綴和後綴來給名字附帶更多信息
  • 決定名字的長度

    • 在小的作用域裏可以使用短的名字
  • 利用名字的格式來表達含義

第3章 不會誤解的名字

  • filter是個二義性單詞。我們不清楚它的含義到底是“挑出”還是“減掉”。最好避免使用“filter”這個名字,因爲它太容易誤解。
  • 推薦用min和max來表示(包含)極限
  • 建議 命名極限最清楚的方式是在要限制的東西前加上max_或者min_。
  • 推薦用first和last來表示包含的範圍
  • 推薦用begin和end來表示包含/排除範圍
  • 通常來講,加上像is、has、can或should這樣的詞,可以把布爾值變得更明確。
  • 很多程序員都習慣了把以get開始的方法當做“輕量級訪問器”這樣的用法,它只是簡單地返回一個內部成員變量。如果違背這個習慣很可能會誤導用戶。相反,這個方法應當重命名爲像computeMean()這樣的名字,後者聽起來更像是有些代價的操作。

第4章 審美

大家都願意讀有美感的代碼。通過把代碼用一致的、有意義的方式“格式化”,可以把代碼變得更容易讀,並且可以讀得更快。下面是討論過的一些具體技巧:

  • 如果多個代碼塊做相似的事情,嘗試讓它們有同樣的剪影,使用一致的佈局,讓讀者很快就習慣這種風格
  • 把代碼按“列”對齊可以讓代碼更容易瀏覽
  • 如果在一段代碼中提到A、B和C,那麼不要在另一段中說B、C和A。選擇一個有意義的順序,並始終用這樣的順序
  • 用空行來把大塊代碼分成邏輯上的“段落”。

第5章 該寫什麼樣的註釋

註釋的目的是幫助讀者瞭解作者在寫代碼時已經知道的那些事情。本章介紹瞭如何發現所有的並不那麼明顯的信息塊並且把它們寫下來。什麼地方不需要註釋:

  • 能從代碼本身中迅速地推斷的事實
  • 用來粉飾爛代碼(例如蹩腳的函數名)的“柺杖式註釋”——應該把代碼改好。你應該記錄下來的想法包括:

    • 對於爲什麼代碼寫成這樣而不是那樣的內在理由(“指導性批註”)
    • 代碼中的缺陷,使用像TODO:或者XXX:這樣的標記
    • 常量背後的故事,爲什麼是這個值。
    • 站在讀者的立場上思考:預料到代碼中哪些部分會讓讀者說:“啊?”並且給它們加上註釋
    • 爲普通讀者意料之外的行爲加上註釋
    • 在文件/類的級別上使用“全局觀”註釋來解釋所有的部分是如何一起工作的
    • 用註釋來總結代碼塊,使讀者不致迷失在細節中

第6章 寫出言簡意賅的註釋

本章是關於如何把更多的信息裝入更小的空間裏。下面是一些具體的提示:

  • 當像“it”和“this”這樣的代詞可能指代多個事物時,避免使用它們
  • 儘量精確地描述函數的行爲
  • 在註釋中用精心挑選的輸入/輸出例子進行說明
  • 聲明代碼的高層次意圖,而非明顯的細節
  • 用嵌入的註釋(如Function(/arg =/...))來解釋難以理解的函數參數
  • 用含義豐富的詞來使註釋簡潔

第二部分 簡化循環和邏輯

第7章 把控制流變得易讀

有幾種方法可以讓代碼的控制流更易讀

  • 在寫一個比較時(while (bytes_expected > bytes_received)),把改變的值寫在左邊並且把更穩定的值寫在右邊更好一些(while (bytes_received <; bytes_expected))
  • 你也可以重新排列if/else語句中的語句塊。通常來講,先處理正確的/簡單的/有趣的情況。有時這些準則會衝突,但是當不衝突時,這是要遵循的經驗法則
  • 某些編程結構,像三目運算符(:?)、do/while循環,以及goto經常會導致代碼的可讀性變差。最好不要使用它們,因爲總是有更整潔的代替方式
  • 嵌套的代碼塊需要更加集中精力去理解。每層新的嵌套都需要讀者把更多的上下文“壓入棧”。應該把它們改寫成更加“線性”的代碼來避免深嵌套。通常來講提早返回可以減少嵌套並讓代碼整潔。“保護語句”(在函數頂部處理簡單的情況時)尤其有用。

第8章 拆分超長表達式

  • 把超長表達式拆成更容易理解的小塊
  • 引入解釋變量

第9章 變量與可讀性

關於程序中的變量是如何快速累積而變得難以跟蹤的。你可以通過減少變量的數量和讓它們儘量“輕量級”來讓代碼更有可讀性。具體有:

  • 減少變量,即那些妨礙的變量。我們給出了幾個例子來演示如何通過立刻處理結果來消除“中間結果”變量
  • 減小每個變量的作用域,越小越好。把變量移到一個有最少代碼可以看到它的地方。眼不見,心不煩
  • 只寫一次的變量更好。那些只設置一次值的變量(或者const、final、常量)使得代碼更容易理解。

第三部分 重新組織代碼

我們會講到三種組織代碼的方法:

  • 抽取出那些與程序主要目的“不相關的子問題”
  • 重新組織代碼使它一次只做一件事情
  • 先用自然語言描述代碼,然後用這個描述來幫助你找到更整潔的解決方案

第10章 抽取不相關的子問題

  • 所謂工程學就是關於把大問題拆分成小問題再把這些問題的解決方案放回一起。把這條原則應用於代碼會使代碼更健壯並且更容易讀。
  • 積極地發現並抽取出不相關的子邏輯

    • 純工具代碼

      • 通常來講,如果你在想:“我希望我們的庫裏有XYZ()函數”,那麼就寫一個!(如果它還不存在的話)經過一段時間,你會建立起一組不錯的工具代碼,後者可以應用於多個項目。
    • 其他多用途代碼

      • 當format_pretty()中的代碼自成一體後改進它變得更容易。當你在使用一個獨立的小函數時,感覺添加功能、改進可讀性、處理邊界情況等都更容易。
    • 創建大量通用代碼
    • 項目專有的功能
    • 簡化已有接口
    • 按需重塑接口
    • 過猶不及

      • 引入這麼多小函數實際上對可讀性是不利的,因爲讀者要關注更多東西,並且按照執行的路徑需要跳來跳去

第11章 一次只做一件事

  • 應該把代碼組織得一次只做一件事情。

第12章 把想法變成代碼

  • 用自然語言描述程序然後用這個描述來幫助你寫出更自然的代碼。這個技巧出人意料地簡單,但很強大。看到你在描述中所用的詞和短語還可以幫助你發現哪些子問題可以拆分出來。 但是這個“用自然語言說事情”的過程不僅可以用於寫代碼。

第13章 少寫代碼

  • 不是所有的程序都需要運行得快,100%準確,並且能處理所有的輸入。如果你真的仔細檢查你的需求,有時你可以把它削減成一個簡單的問題,只需要較少的代碼。
  • 我們所描述的是宇宙的自然法則——隨着任何座標系統的增長,把它粘合在一起所需的複雜度增長得更快。 最好的解決辦法就是“讓你的代碼庫越小,越輕量級越好”,就算你的項目在增長。那麼你就要:

    • 創建越多越好的“工具”代碼來減少重複代碼(見第10章)
    • 減少無用代碼或沒有用的功能
    • 讓你的項目保持分開的子項目狀態
    • 總的來說,要小心代碼的“重量”。讓它保持又輕又靈。
  • 熟悉你周邊的庫

    • 很多時候,程序員就是不知道現有的庫可以解決他們的問題。或者有時,它們忘了庫可以做什麼。知道你的庫能做什麼以便你可以使用它,這一點很重要。

我的總結

在日常開發中,比較注重編碼規範及命名等細節,個人認爲命名和註釋寫得好是需要觀察、積累和總結的,這也很重要,同時在閱讀jdk、spring、mybatis等優秀框架源碼也發現好的命名的重要性,下面總結了一些命名及編碼方式,周知的駝峯命名、常量大寫等就不列舉了。只寫一些書中沒提到的。

命名相關

  • 動賓格式命名方法

    • prepareContext、prepareEnvironment
  • 前綴命名

    • spring中doXXX是真正做事情的方法,如doLoadBeanDefinitions、doRegisterBeanDefinitions
    • preXXX、postXXX:前置、後置處理方法
    • loadXXX:loadBeanDefinitions
  • 後綴命名

    • XXXListener:一看就知道是監聽器
    • XXXFactory、XXXDelegate、XXXTemplate:使用了工廠模式、委派模式、模板模式等
  • 善用詞性

    • listeners.starting()、listeners.started(context)等不用說就知道區別,表示不同階段
  • 名詞單複數

    • user、users:分別代表一個用戶和多個用戶

代碼風格

  • 按功能劃分代碼,一個類不超過千行
  • 常量統一寫在頭部
  • 多次使用的抽象成工具類
  • 語義化、函數式編程
更多信息可以關注我的個人博客:逸竹小站逸竹小站

也歡迎關注我的公衆號:yizhuxiaozhan,二維碼:公衆號二維碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章