天天看點

【日常積累 - 04】《編寫可讀代碼的藝術》- 寫出優雅的代碼前言第1章 代碼應該易于了解第一部分 表面層次的改進第二部分 簡化循環和邏輯第三部分 重新組織代碼第四部分 精選話題

​​最近看了《編寫可讀代碼的藝術》收獲頗豐,剛好這本書的讀書筆記适合放在這裡。程式員的一大樂趣就是可以自己決定創作出的東西,而代碼的可讀性、代碼是否優雅,都決定了你作品的好壞,想要好的作品嗎?那就想辦法寫出更優美的代碼吧。

目錄

前言

第1章 代碼應該易于了解

第一部分 表面層次的改進

第2章 把資訊裝到名字裡

第3章 不會誤解的名字

第4章 審美

第5章 該寫什麼樣的注釋

第6章 寫出言簡意赅的注釋

第二部分 簡化循環和邏輯

第7章 把控制流變得易讀

第8章 拆分超長的表達式

第9章 變量與可讀性

第三部分 重新組織代碼

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

第11章 一次隻做一件事

第12章 把想法變成代碼

第13章 少寫代碼

第四部分 精選話題

第14章 測試與可讀性

第15章 設計并改進“分鐘/小時計數器”

前言

程式員之間的互相尊重和對工作的尊重都展現在代碼中。

代碼是寫給人讀的,應該便于了解,優質的代碼可以讓協作變得高效。

學習編寫好代碼的習慣,并應用于實踐中。

第1章 代碼應該易于了解

  • 代碼的寫法應該使别人了解它所需的時間最小化。
  • 要多想一想目前在寫的代碼别人是否容易了解它。

第一部分 表面層次的改進

  • 代碼風格
  • 命名
  • 排版
  • 注釋

第2章 把資訊裝到名字裡

  • 選擇專業的詞。
  • 避免泛泛的名字。
  • 用具體的名字代替抽象的名字。
  • 使用字首或者字尾附加更多資訊。
    • 增加機關。如time_cost_day, time_cost_ms
    • 目前資料格式。如i_days,f_days。
    • 未處理的變量前面加raw_等
  • 控制名字長度的依據
    • 如果變量作用域小,則可以使用較短的名字。如果變量作用域較大,則盡量使用資訊完備的名字。
    • 采用縮寫的原則是:團隊新成員能否了解這個命名
  • 利用名字的格式來表達含義
    • 可參考Google C++代碼風格
    • 類名、函數名首單詞大寫
    • 宏全大寫
    • 變量名全小寫,用下劃線隔開
    • 成員變量最後可以加下劃線等,如CountChars_
  • 如果一個flag要用于多個模式選擇,那麼就拆分flag,給他們不同的命名。

第3章 不會誤解的名字

  • 多問自己幾遍,這個名字會被别人解讀為其他含義嗎?
  • 命名盡量具體
  • 用min、max表示包含的極限
  • 用first和last表示閉區間(stop表示右開,last是右閉)
  • 用begin和end表示左閉右開區間
  • 給布爾值命名
    • bool read_password = true,不能确定是需要讀密碼還是已經讀取了密碼
    • bool need_password = true會更好。
    • 通常加上is、has、can、should這樣的詞可以讓布爾值更加明确。
  • 命名需要與使用者的期望相比對
    • 如GetMean(),可能是直接擷取已經計算好的mean,也可能是循環計算mean。改成ComputeMean會更好。
    • 如調用size()可以改為CountSize()。

第4章 審美

  • 保持自己風格一緻
  • 讓相似的代碼看上去相似
  • 把相關的代碼行分組,形成代碼塊
  • 列對齊:整潔易讀,容易發現錯誤。
  • 選擇一個有意義的順序,始終一緻的使用它。如:
    • 從最重要,到最不重要排序。
    • 按字母順序排序
  • 用空行把代碼分成邏輯上的段落
    • 相似的想法放在一起并且與其他想法分開
    • 提供可見的注釋腳印
    • 便于段落間導航
  • 保持風格一緻性(一緻的風格比正确的風格更重要)

第5章 該寫什麼樣的注釋

  • 注釋的目的是盡量幫助讀者了解的和作者一樣多。
  • 什麼情況下不需要注釋
    • 不要為那些通過代碼本身就能快速推斷的資訊做注釋。
    • 一個好的名字比好的注釋重要,不要給不好的名字加注釋,應該把名字改好。
    • 好代碼 > 壞代碼+好注釋
  • 用代碼記錄你的思想
    • 加入導演評論。記錄自己的思考。
    • 為代碼中的瑕疵寫注釋。
      • 增加TODO,FIXME,HACK等
  • 站在讀者的角度,去想象他們需要什麼

第6章 寫出言簡意赅的注釋

  • 注釋應該有很高的(資訊/空間)使用率
  • 讓注釋保持緊湊,要保持注釋的簡潔
  • 避免使用不明确的代詞
  • 潤色粗糙的句子。
  • 精确的描述函數的行為
  • 用輸入、輸出的示例來說明特别的情況。
  • 聲明代碼的意圖。有時候這種注釋扮演了備援檢查的角色。
  • 對于不好了解的實參,标明形參名。
  • 采用資訊含量高的詞。
    • 有些普遍的問題和解決方案會重複出現。通常會有專門的詞或者短語代指這些場景、模式。建議采用這些值。

第二部分 簡化循環和邏輯

  • 控制流
  • 邏輯表達式

第7章 把控制流變得易讀

  • 把條件、循環以及其他對控制流的改變做的越符合直覺越好。讓讀者不用反複的去看一段代碼。
  • 條件語句中參數順序的選擇
    • 會變化的值放在比較的左側,常量值放在比較的右側。 
      • if ( length > 10) 比 if ( 10 <= length ) 更符合直覺
      • while ( bytes_received < bytes_excepted ) 比 while ( bytes_excepted > bytes_received ) 更符合直覺。
      • 這種做法符合我們的思維習慣:“如果你的年齡不小于18歲”,“如果18歲大于等于你的年齡”。
  • if else 語句塊的順序,先判斷哪種情況?
    • 首先處理正邏輯的情況,如 if (debug) 而不是 if(!debug)
    • 先處理掉簡單的情況。
    • 先處理特例或者複雜情況。
  • ? : 條件表達式的使用場景
    • 如果想使用三目運算符來減少代碼行數,需要判斷采用三目運算符是否會導緻讀者了解它的成本增加。
    • 建議:預設情況都用 if else,三目運算符隻在最簡單的情況下使用。
  • 在一個函數中,不同情況可以有不同的return語句
  • 不要用go/to
  • 不要用do/while
  • 最小化花括号嵌套
    • 嵌套出現的原因:對于寫代碼的人來說,他是從已有的條件裡面找到了一個适合增加新嵌套的地方,對他來說很好了解和直覺。但是對于看代碼的人來說,複雜的嵌套是一個整體,思維需要頻繁出入棧,不易了解。
    • 避免嵌套:每次增加嵌套都從一個全新的角度來審視,不要基于自己已有的了解。
    • 通過提前return盡快離開嵌套。
  • 減少控制流使用的次數
    • 如線程、中斷處理、異常、函數指針和匿名函數、虛方法。過多的采用會導緻控制流難以了解,盡量将這些操作隔離開。

第8章 拆分超長的表達式

  • 把超長的表達式拆分成容易了解的小塊。
  • 增加一個用于解釋中間值的變量。
  • 容易了解的表達式,如果需要反複出現,也可以用變量代替。
  • 利用德摩根定理将判斷語句等價轉換,有時候可以增加可讀性。
  • 将短路邏輯改的直覺
    • 短路邏輯即:( if a == True || b == True ); 如果a == True為真,則 b == True不會執行 
    • 如 assert ( ( !( bucket = FindBucket(key) )) || !bucket -> IsOccupied() );
    • 可以改為
      • bucket = FindBucket(key);
      • if (bucket != Null) assert ( !bucket -> IsOccupied() );
    • 以直覺為主,有時候短路邏輯反而更直覺,如從a, b, c中找出第一個為真的值
      • x = a || b || c
  • 反向思考,對于長判斷表達式是否有更優雅的、判斷它的反情況的方式。

第9章 變量與可讀性

  • 減少變量個數
    • 減少沒有價值的臨時變量。
    • 減少用于儲存中間結果的變量。
    • 減少用于控制流的變量。
  • 縮小變量的作用域
    • 避免使用全局變量。 
    • 讓你的變量盡量對少的代碼行可見。
    • 盡量使用靜态方法。
    • 把變量的定義移動到使用它之前,不要在檔案開始去定義所有變量。
    • 在C++中多使用const,在Java中多使用final。這樣讀者就不用思考這個變量的改動。
    • 操作一個變量的地方越多,越難确定它的目前值。

第三部分 重新組織代碼

  • 組織函數。
  • 抽取出那些與程式主要目的不相關的子問題。
  • 重新組織代碼使其一次隻做一件事情。
  • 先用自然語言描述代碼,然後用這個描述來找到更簡潔的解決方案。

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

  • 工程學就是把大問題拆分成小問題,再把這些小問題的解決方案放到一起。
  • 如果有一些代碼在目前函數中正在解決與目前函數無關的子問題,那麼就單獨抽出來實作一個函數。
  • 不相關的子問題完全是自包含的,不用關心其他人怎麼調用它。
  • 如果你希望有一個工具來解決現在的某個問題,那就去實作它。
  • 将代碼子產品化。
  • 建立大量通用式代碼,自成一庫。
  • 簡化接口。
  • 過猶不及,不要過分拆函數,以可讀性為主。

第11章 一次隻做一件事

  • 将初始化對象、處理輸入、邏輯等互相分開。
  • 一個代碼塊一次隻做一件事,也可以根據需要将其拆分成不同函數。
  • 任務可以分的很細。

第12章 把想法變成代碼

  • 如果你不能讓一個完全沒有基礎的人把你所做的事情聽明白,那麼你就沒有真正了解這件事。
  • 代碼就是你想法的實作,讀者需要從你的實作中了解你的代碼
    • 像對着同僚一樣用自然語言描述代碼要做什麼。
    • 注意描述中所用的關鍵詞和短語。
    • 寫出與描述相比對的代碼。
  • 清楚地描述邏輯。
  • 了解你所用的函數庫。

第13章 少寫代碼

  • 最好讀的代碼就是沒有代碼。
  • 功能越簡潔越好。
  • 質疑和拆分你的需求,它真的需要這個功能嗎?
    • 減少需求。
    • 解決更簡單的問題。
  • 代碼庫越小、越輕量越好。
    • 建立越多越好的工具代碼來減少重複代碼。
    • 減少無用代碼或者無用功能。
    • 讓項目保持獨立的子項目狀态。
  • 熟悉你所使用的庫,很多你需要的功能它們已經實作了。
    • 每隔一段時間,花15分鐘閱讀标準庫中的所有函數、子產品、類型名。并不是為了記住,而是需要的時候有個印象,知道無需自己實作。
  • 代碼越多就越難維護。
  • 不要過度設計。

第四部分 精選話題

  • 作者精選出的兩個應用示例。

第14章 測試與可讀性

  • 對使用者隐去不重要的細節,以便更重要的細節更加突出。
  • 讓列印資訊具有可讀性。
  • 了解常用的一些庫。如python中的unittest。

第15章 設計并改進“分鐘/小時計數器”

  • 一個更加具體的案例。
部落客會持續更新一些深度學習相關的基礎知識以及工作中遇到的問題和感悟,喜歡請關注、點贊、收藏。

繼續閱讀