《clean code》 閱讀筆記

編者寄語:

這是一本真正的好書,不過若是讀者沒有必定的經驗,以及缺少對編程境界的追求的話,可能認爲這本書很通常。固然,對於有心人來講,這本書裏面的部分東西可能都已經習覺得常了。java

那麼,你是怎樣的呢?程序員

另外我爲何寫的是《clean code》而不是《代碼整潔之道》,由於這本書不少地方你須要看原版的文字才能get到做者真正想表達的意思。若是有能力仍是看原版吧。算法

看原版書,你能學到不少術語表達,在你看外文技術文章的時候更容易幫助你理解全文。如increase cohesion - 增長內聚性,decrease coupling - 減小耦合,separate concerns - 關注點分離,modularize system concerns - 模塊化系統關注點,這些都是很經典的表達。編程

I sincerely and strongly recommend u to read 《clean code》 rather than 《代碼整潔之道》設計模式

原文地址: 《clean code》 閱讀筆記

轉載請註明出處!緩存

1、整潔代碼 ⭐

關鍵詞: 優雅
  1. 代碼邏輯直接了當,讓缺陷難以隱藏
  2. 儘可能減小依賴關係,使之便於維護
  3. 依據某種分層策略完善錯誤處理代碼
  4. 性能調至最優,免得引誘別人作沒規矩的優化
  5. 整潔的代碼只作一件事
  6. 簡單直接,具備可讀性
  7. 有單元測試和驗收測試
  8. 有意義的命名
  9. 代碼應在字面上表達其含義
  10. 儘可能少的實體:類、方法、函數
  11. 沒有重複代碼
整潔的代碼讀起來使人愉悅

2、有意義的命名 ⭐⭐

  • 使用帶有語義的命名,能 夠讓維護代碼的人更容易理解和修改代碼
  • 編程原本就是一種社會活動(大部分的編程活動都是人與人協做的過程)
  • 避免思惟映射,明確纔是王道
  • 儘量要作到「顧名思義」,看到名稱就能知道這個變量、函數、類、包的意義、用途。

具體規則

  1. 名副其實:名稱不須要註釋補充就可見其含義、用途
  2. 不要寫多餘的廢話或者容易讓人混淆的命名。

    好比"customerObject"和"customer", "ProductInfo"和"ProductData";這種就是意義混雜的廢話。若是真的有區別,就用特定的能夠區分的命名來描述它。安全

  3. 使用讀得出來的名稱。
  4. 使用可搜索的名稱。

    MAX_CLASSES_PER_STUDENT很容易,但想找數字7就麻煩了。網絡

  5. 類名和對象名應該是名詞或名詞短語。
  6. 方法名應當是動詞或動詞短語。

    如postPayment、deletePage或save。屬性訪問器、修改器和斷言應該根據其值命名,並依Javabean標準加上get、set和is前綴。數據結構

  7. 每一個抽象概念選一個詞,而且一以貫之

    個人理解中,在同個領域模型中,就應該只有一個命名,好比訂單號,同個系統中不該該出現TradeNo、OrderNo等多個命名。併發

  8. 儘可能用術語(CS術語,算法,數學術語)命名

    儘管用那些計算機科學(Computer Science,CS)術語、算法名、模式名、數學術語。

  9. 上一條沒法作到的狀況下,儘可能使用源自所涉問題領域的名稱。

    若是不能用程序員熟悉的術語來給手頭的工做命名,就採用從所涉問題領域而來的名稱。

  10. 添加富有意義的語境,例如利用UserInfo類封裝各類我的信息

3、函數 ⭐⭐

編程就像講故事,要用準確、清晰、富有表達力的語句(代碼)
  • 好的函數應該作到自頂向下閱讀代碼時,像是在閱讀報刊文章。
  • 寫代碼很像是寫文章。先想怎麼寫就怎麼寫,而後再打磨:分解函數、修更名稱、消除重複
  • 編程實際上是一門語言設計藝術,大師級程序員把程序系統當作故事來說。使用準確、清晰、富有表達力的代碼來幫助你講故事。

具體規則

  1. 短小!短小!短小

    重要的事情說3遍。

  2. 函數應該作一件事。作好這件事。只作這一件事
  3. 每一個函數一個抽象層級!!!

    這個是編者認爲很是重要的一點,也是本人在開發過程中看到最多的問題。應該處於不一樣抽象層級的代碼混亂在一塊兒時,閱讀和理解起來會很痛苦。

    引原文描述:

    函數中混雜不一樣抽象層級,每每讓人迷惑。讀者可能沒法判斷某個表達式是基礎概念仍是細節。更惡劣的是,就像破損的窗戶,一旦細節與基礎概念混雜,更多的細節就會在函數中糾結起來。

    可是,就像做者說的,這條規則很難

  4. 使用描述性的名稱

    長而具備描述性的名稱,要比短而使人費解的名稱好。長而具備描述性的名稱,要比描述性的長註釋好。

    爲只作一件事的小函數取個好名字。函數越短小、功能越集中,就越便於取個好名字。

  5. 拒絕boolean型標識參數。

    例: CopyUtil.copyToDB(isWorkDB) --> CopyUtil.copyToWorkDB(), CopyUtil.copyToLiveDB()

    (可是編者閱讀不少源碼裏面也沒有遵照,手動狗頭...)

  6. 若是必定須要多個參數,那麼可能須要對參數進行封裝
  7. 使用異常代替返回錯誤碼,錯誤處理代碼就能從主路徑代碼中分離出來獲得簡化。
  8. Don't Repeat Yourself(經典的DRY原則)
  9. 先把函數寫出來,再規範化

4、註釋

這節實際上內容很少,儘可能避免註釋
  • 別給糟糕的代碼加註釋(專家建議不如重寫)
  • 把力氣花在寫清楚明白的代碼上,直接保證無需編寫註釋。
  • 好的註釋:

    • 法律信息
    • 提供信息
    • 解釋意圖
    • 警示
    • TODO註釋

5、格式 ⭐

  • 代碼格式很重要。代碼格式關乎溝通,而溝通是專業開發者的頭等大事。
  • 向報紙格式學習代碼編寫。

具體規則

  1. 垂直距離

    1. 變量聲明應該儘量靠近使用位置,本地變量應該在函數頂部出現
    2. 實體變量應該放在類的頂部聲明
    3. 相關的函數應該放在一塊兒
    4. 函數的排列順序保持其相互調用的順序
  2. 水平位置

    1. 一行代碼儘可能短,不超過100 - 120 個字符。

      這個在常見的IDE中能夠設置提示線。下圖是IDEA的配置位置。

      效果:

    2. 用空格將相關性弱的分開
    3. 聲明和賦值不須要水平對齊
    4. 注意縮進
  3. 團隊之間造成一致的代碼格式規範(Checkstyle 插件瞭解一下?)

    不要使用不一樣的風格來編寫源代碼,會增長其複雜度。

6、對象與數據結構

這塊有兩個我比較在乎的概念
  • 要弄清楚數據結構和對象的差別:對象把數據隱藏於抽象以後,曝露操做數據的函數。數據結構曝露其數據,沒有提供有意義的函數
  • The Law of Demeter:模塊不該瞭解它所操做對象的內部情形。

    更準確更白話地說:方法不該調用由任何函數返回的對象的方法。只跟朋友談話,不與陌生人談話。

    反例:

    final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();

7、錯誤處理 ⭐⭐

  • 一個原則:錯誤處理很重要,可是若是它搞亂了代碼邏輯,就是錯誤的作法
  • 整潔代碼是可讀的,但也要強固。可讀與強固並不衝突。若是將錯誤處理隔離看待,獨立於主要邏輯以外,就能寫出強固而整潔的代碼。作到這一步,咱們就能單獨處理它,也極大地提高了代碼的可維護性。

具體規則

  1. 使用異常而非返回碼
  2. 使用不可控異常(這點深有體會,checked Exception的代價很大)

    這裏做者想說明的是,在使用受檢異常時,你首先要考慮這樣是否能值回票價。由於受檢異常違反了開閉原則,當你在一個方法內拋出了受檢異常時,你就得在catch語句和拋出異常之間的方法調用鏈中的每一個方法簽名中聲明這個異常。

    這意味着,你對軟件較低層級的修改,會涉及到較高層級的簽名。封裝被打破了,由於在拋出路徑中的每一個函數都要去了解下一層級的異常細節。既然異常旨在讓你能在較遠到處理錯誤,可控異常以這種方式破壞封裝簡直就是一種恥辱

    若是你在編寫一套關鍵代碼庫,則可控異常有時也會有用:你必須捕獲異常。但對於通常的應用開發,其依賴成本要高於收益。
  3. 給出異常發生的環境說明(這個也很重要)

    建立信息充分的錯誤消息,並和異常一塊兒傳遞出去。在消息中,包括失敗的操做和失敗類型。若是你的應用程序有日誌系統,傳遞足夠的信息給catch塊,並記錄下來。

    良好的日誌和異常機制,是不該該出現調試的。打日誌和拋異常,必定要把上下文給出來,不然,等於在毀滅命案現場,把後邊處理問題的人,往歪路上帶。

    須要調試來查找錯誤時,每每是一種對異常處理機制的侮辱

  4. 使用通用異常類打包第三方API包的異常(如調用一些第三方支付SDK等)
  5. 嘗試使用特例模式(SPECIAL CASE PATTERN),將異常行爲封裝到特例對象中。

    很巧妙高級的一種設計模式。

    // 修改前
    try {
        MealExpenses expenses = expenseReportDAO.getMeals(employee.getID());
        m_total += expenses.getTotal();
    } catch(MealExpensesNotFound e) {
        m_total += getMealPerDiem();
    }
    
    
    // 優化以後,當沒有餐食消耗(即上述代碼拋出MealExpensesNotFound的狀況),返回特例對象
    MealExpenses expenses = expenseReportDAO.getMeals(employee.getID());
    m_total += expenses.getTotal();
    
    // 特例對象
    public class PerDiemMealExpenses implements MealExpenses {
        public int getTotal() {
        // return the per diem default
        }
    }
  6. 不要返回null,不要傳遞null

    相信很多程序員都深受null 的困擾。返回null值,基本上是在給本身增長工做量,也是在給調用者添亂。只要有一處沒檢查null值,應用程序就會失控。

    在大多數編程語言中,沒有良好的方法能對付由調用者意外傳入的null值。事已如此,恰當的作法就是禁止傳入null值。

8、邊界

邊界這一章我的讀起來比較難懂。感受像是翻譯的問題。

原書這一章節的名字叫作"Boundaries"。

這一章篇幅較短,意義有點難懂,這裏簡單總結:做者的意思是讓咱們本身的代碼和第三方庫的代碼不要耦合太緊密,須要有清晰的Boundaries。

同時也給出了第三方類庫的學習建議:探索性地學習測試,以此熟悉類庫,寫出良好的代碼。

9、單元測試

  • 測試代碼和生產代碼同樣重要。它可不是二等公民。

    它須要被思考、被設計和被照料。它該像生產代碼通常保持整潔。

    測試代碼須要隨着生產代碼的演進而修改,若是測試不能保持整潔,只會愈來愈難修改。

  • 整潔的測試有什麼要素?有三個要素:可讀性,可讀性和可讀性
  • 每一個測試一個斷言,每一個測試一個概念。

單測自己也應該成爲Code Review的一部分,單測寫的好,bug必定少。

TDD 三定律

  • 定律一 在編寫不能經過的單元測試前,不可編寫生產代碼。
  • 定律二 只可編寫恰好沒法經過的單元測試,不能編譯也算不經過。
  • 定律三 只可編寫恰好足以經過當前失敗測試的生產代碼。
任何一種迭代和增量的交付方式,都會遇到一個嚴肅的靈魂拷問: 頻繁對軟件作修改,如何保障軟件不被改壞?這個問題,用人肉測試解決不了。交付越頻繁,人肉測試就越不可能跟上節奏。自動化的、快速且可靠的、覆蓋完善的測試必不可少。這種要求,後補式的、黑盒的測試方法不可能達到,必須在開發軟件的過程當中內建。

當團隊被迫採用迭代和增量的需求管理和項目管理方式,對應的配置管理和質量保障手段就必須跟上。TDD不是錦上添花,而是迭代和增量交付不可或缺的基石

F.I.R.S.T.

整潔的測試應該遵循如下5條規則:

  • 快速(Fast)

    測試應該夠快。測試應該能快速運行。測試運行緩慢,你就不會想要頻繁地運行它。若是你不頻繁運行測試,就不能儘早發現問題,也沒法輕易修正,從而也不能垂手可得地清理代碼。最終,代碼就會腐壞。

  • 獨立(Independent)

    測試應該相互獨立。某個測試不該爲下一個測試設定條件。你應該能夠單獨運行每一個測試,及以任何順序運行測試。當測試互相依賴時,頭一個沒經過就會致使一連串的測試失敗,使問題診斷變得困難,隱藏了下級錯誤。

  • 可重複(Repeatable)

    測試應當可在任何環境中重複經過。你應該可以在生產環境、質檢環境中運行測試,也可以在無網絡的列車上用筆記本電腦運行測試。若是測試不能在任意環境中重複,你就總會有個解釋其失敗的接口。當環境條件不具有時,你也會沒法運行測試。

  • 自足驗證(Self-Validating)

    測試應該有布爾值輸出。不管是經過或失敗,你不該該查看日誌文件來確認測試是否經過。你不該該手工對比兩個不一樣文本文件來確認測試是否經過。若是測試不能自足驗證,對失敗的判斷就會變得依賴主觀,而運行測試也須要更長的手工操做時間

  • 及時(Timely)

    測試應及時編寫。單元測試應該剛好在使其經過的生產代碼以前編寫。若是在編寫生產代碼以後編寫測試,你會發現生產代碼難以測試。你可能會認爲某些生產代碼自己難以測試。你可能不會去設計可測試的代碼

10、類 ⭐⭐

類應該儘可能短小

對於衡量類的大小,這裏書中提出了一個不一樣的衡量方法:計算權責。我理解的意思就是,一個類承擔了太多的權責以後,這個類就算大了。

因此書中隨即提出了SRP - 單一權責原則(也叫單一職責原則)

單一權責原則

單一權責原則(SRP)認爲,類或模塊應有且只有一條加以修改的理由。該原則既給出了權責的定義,又是關於類的長度的指導方針。類只應有一個權責——只有一條修改的理由。

做者還提到了,系統應該由許多短小的類而不是少許巨大的類組成。每一個小類封裝一個權責,只有一個修改的緣由,並與少數其餘類一塊兒協同達成指望的系統行爲。

內聚

同時,做者提出了保持內聚性就會獲得許多短小的類。

類的高內聚的含義是:類的實體變量應儘量少,類中方法儘量多地使用到這些變量。(若是一個類中的每一個變量都被每一個方法所使用,則該類具備最大的內聚性)

組織類時考慮代碼的修改

在整潔的系統中,咱們對類加以組織,以下降修改的風險。

  • 開放-閉合原則(OCP)

    類應當對擴展開放,對修改封閉。經過子類化手段,類對添加新功能是開放的,並且能夠同時不觸及其餘類。

  • 依賴倒置原則(Dependency Inversion Principle,DIP)

    DIP認爲類應當依賴於抽象而不是依賴於具體細節。經過這種抽象隔離了系統之間的元素,使得系統每一個元素的理解變得更加容易,使用起來更加靈活、更加可複用。

11、系統

系統構造與使用分開。

這裏我理解就是將一些對象實例的初始化和使用分離解耦,將構建實例的邏輯交給一個公共的模塊/類/框架來作。這裏做者也介紹了開發中常見的兩種方式,體現了這種思想:

  • 工廠:使用工廠方法自行決定什麼時候建立實例,可是構造細節卻在其餘地方
  • 依賴注入:當A對B有依賴時,A中不負責B的實例化(這就是類的權責單一原則

後半章主要講的是AOP的思想和具體的框架實現。就是說將一些重複性、功能性的代碼(如:性能監視、日誌記錄、事務管理、安全檢查、緩存等)進行關注面切分,模塊化,成就了分散化管理和決策。最終的效果也顯而易見,減小了重複代碼,關注面的分離也使得設計、決策更加清晰容易。

12、Emergence (迭進)

這一節主要是講了四個簡單的設計規則(design rules),經過遵循這四個規則,你能夠編寫出很好的代碼,深刻了解代碼的結構和設計,繼而以一種更簡單的方式來學習掌握SRP和DIP之類的設計原則。

Four rules of Simple Design are of significant help increating well-designed software
  • 運行全部的測試

    全面測試並持續經過全部測試。遵循SRP的類,測試起來較爲簡單。測試編寫得越多,就越能持續走向編寫較易測試的代碼。因此,確保系統徹底可測試能幫助咱們建立更好的設計。

    有了全面的測試保駕護航以後,咱們就有條件一步一步地去重構完善咱們的代碼,目的是爲了獲得「高內聚,低耦合」的系統。書中也提出了下面三條簡單的規則。

  • 不要重複(DRY)
  • 寫出能清晰表達編碼者意圖的代碼(Expressive)
  • 儘可能減小類和方法(Mininal Classes and Methods)

    當你在重構時,按照SRP、代碼可讀性等規則遵照,是有可能建立出比原來更多的細小的類。但這不在本條的針對範圍以內。

    這裏的儘可能減小,做者舉例了一種狀況,就是毫無心義的教條主義會致使編碼人員無心識的建立不少的類和方法。不知道你有沒有相似的經歷,我拿我親身體會舉個例子,我很難理解在某個項目中,對一個領域對象(如User),在構建對應的Service層和Dao層的時候,必定要爲每一個類建立接口,即便這些接口根本不可能有其餘的實現類。

十3、併發

「Objects are abstractions of processing. Threads are abstractions of schedule.」

—James O. Coplien

這一節做者討論了併發編程的需求和難點,而且給出了一些解決這些困難和編寫整潔併發代碼的建議。由於關於併發編程有更好的資料能夠學習,因此這裏我就簡單總結一下。

併發防護原則

  • 單一權責原則(SRP):方法/類/組件應當只有一個修改的理由
  • 限制數據做用域:嚴格限制對可能被共享的數據的訪問
  • 使用數據複本:這點很好理解,避免數據的共享。(Java 中的ThreadLocal)
  • 線程應儘量獨立:不與其餘線程共享數據。每一個線程處理一個客戶端請求,從不共享的源頭接納全部請求數據,存儲爲本地變量

小結

其餘未提到的章節,是我以爲相較來講非重點的章節。還有可能會有一些內容的遺漏,由於這本書中的精華,我以爲我還須要學習領會。

好書常讀常新,這本書就在個人工位上,我但願在經歷一段時間的工做實踐以後,再次打開這本書,我能有更多更新的一些感悟。

若是本文有幫助到你,但願能點個贊,這是對個人最大動力🤝🤝🤗🤗。
相關文章
相關標籤/搜索