編者寄語:這是一本真正的好書,不過若是讀者沒有必定的經驗,以及缺少對編程境界的追求的話,可能認爲這本書很通常。固然,對於有心人來講,這本書裏面的部分東西可能都已經習覺得常了。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》 閱讀筆記轉載請註明出處!緩存
關鍵詞: 優雅
整潔的代碼讀起來使人愉悅
好比"customerObject"和"customer", "ProductInfo"和"ProductData";這種就是意義混雜的廢話。若是真的有區別,就用特定的能夠區分的命名來描述它。安全
找MAX_CLASSES_PER_STUDENT
很容易,但想找數字7就麻煩了。網絡
如postPayment、deletePage或save。屬性訪問器、修改器和斷言應該根據其值命名,並依Javabean標準加上get、set和is前綴。數據結構
個人理解中,在同個領域模型中,就應該只有一個命名,好比訂單號,同個系統中不該該出現TradeNo、OrderNo等多個命名。併發
儘管用那些計算機科學(Computer Science,CS)術語、算法名、模式名、數學術語。
若是不能用程序員熟悉的術語來給手頭的工做命名,就採用從所涉問題領域而來的名稱。
編程就像講故事,要用準確、清晰、富有表達力的語句(代碼)
重要的事情說3遍。
每一個函數一個抽象層級!!!
這個是編者認爲很是重要的一點,也是本人在開發過程中看到最多的問題。應該處於不一樣抽象層級的代碼混亂在一塊兒時,閱讀和理解起來會很痛苦。
引原文描述:函數中混雜不一樣抽象層級,每每讓人迷惑。讀者可能沒法判斷某個表達式是基礎概念仍是細節。更惡劣的是,就像破損的窗戶,一旦細節與基礎概念混雜,更多的細節就會在函數中糾結起來。
可是,就像做者說的,這條規則很難。
長而具備描述性的名稱,要比短而使人費解的名稱好。長而具備描述性的名稱,要比描述性的長註釋好。
爲只作一件事的小函數取個好名字。函數越短小、功能越集中,就越便於取個好名字。
例: CopyUtil.copyToDB(isWorkDB) --> CopyUtil.copyToWorkDB(), CopyUtil.copyToLiveDB()
(可是編者閱讀不少源碼裏面也沒有遵照,手動狗頭...)
這節實際上內容很少,儘可能避免註釋
好的註釋:
垂直距離
水平位置
這個在常見的IDE中能夠設置提示線。下圖是IDEA的配置位置。
效果:
不要使用不一樣的風格來編寫源代碼,會增長其複雜度。
這塊有兩個我比較在乎的概念
The Law of Demeter:模塊不該瞭解它所操做對象的內部情形。
更準確更白話地說:方法不該調用由任何函數返回的對象的方法。只跟朋友談話,不與陌生人談話。
反例:
final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();
使用不可控異常(這點深有體會,checked Exception的代價很大)
這裏做者想說明的是,在使用受檢異常時,你首先要考慮這樣是否能值回票價。由於受檢異常違反了開閉原則,當你在一個方法內拋出了受檢異常時,你就得在catch語句和拋出異常之間的方法調用鏈中的每一個方法簽名中聲明這個異常。
這意味着,你對軟件較低層級的修改,會涉及到較高層級的簽名。封裝被打破了,由於在拋出路徑中的每一個函數都要去了解下一層級的異常細節。既然異常旨在讓你能在較遠到處理錯誤,可控異常以這種方式破壞封裝簡直就是一種恥辱。
若是你在編寫一套關鍵代碼庫,則可控異常有時也會有用:你必須捕獲異常。但對於通常的應用開發,其依賴成本要高於收益。
給出異常發生的環境說明(這個也很重要)
建立信息充分的錯誤消息,並和異常一塊兒傳遞出去。在消息中,包括失敗的操做和失敗類型。若是你的應用程序有日誌系統,傳遞足夠的信息給catch塊,並記錄下來。
良好的日誌和異常機制,是不該該出現調試的。打日誌和拋異常,必定要把上下文給出來,不然,等於在毀滅命案現場,把後邊處理問題的人,往歪路上帶。須要調試來查找錯誤時,每每是一種對異常處理機制的侮辱
嘗試使用特例模式(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 } }
不要返回null,不要傳遞null
相信很多程序員都深受null 的困擾。返回null值,基本上是在給本身增長工做量,也是在給調用者添亂。只要有一處沒檢查null值,應用程序就會失控。
在大多數編程語言中,沒有良好的方法能對付由調用者意外傳入的null值。事已如此,恰當的作法就是禁止傳入null值。
邊界這一章我的讀起來比較難懂。感受像是翻譯的問題。
原書這一章節的名字叫作"Boundaries"。
這一章篇幅較短,意義有點難懂,這裏簡單總結:做者的意思是讓咱們本身的代碼和第三方庫的代碼不要耦合太緊密,須要有清晰的Boundaries。
同時也給出了第三方類庫的學習建議:探索性地學習測試,以此熟悉類庫,寫出良好的代碼。
它須要被思考、被設計和被照料。它該像生產代碼通常保持整潔。
測試代碼須要隨着生產代碼的演進而修改,若是測試不能保持整潔,只會愈來愈難修改。
單測自己也應該成爲Code Review的一部分,單測寫的好,bug必定少。
任何一種迭代和增量的交付方式,都會遇到一個嚴肅的靈魂拷問: 頻繁對軟件作修改,如何保障軟件不被改壞?這個問題,用人肉測試解決不了。交付越頻繁,人肉測試就越不可能跟上節奏。自動化的、快速且可靠的、覆蓋完善的測試必不可少。這種要求,後補式的、黑盒的測試方法不可能達到,必須在開發軟件的過程當中內建。當團隊被迫採用迭代和增量的需求管理和項目管理方式,對應的配置管理和質量保障手段就必須跟上。TDD不是錦上添花,而是迭代和增量交付不可或缺的基石。
整潔的測試應該遵循如下5條規則:
測試應該夠快。測試應該能快速運行。測試運行緩慢,你就不會想要頻繁地運行它。若是你不頻繁運行測試,就不能儘早發現問題,也沒法輕易修正,從而也不能垂手可得地清理代碼。最終,代碼就會腐壞。
測試應該相互獨立。某個測試不該爲下一個測試設定條件。你應該能夠單獨運行每一個測試,及以任何順序運行測試。當測試互相依賴時,頭一個沒經過就會致使一連串的測試失敗,使問題診斷變得困難,隱藏了下級錯誤。
測試應當可在任何環境中重複經過。你應該可以在生產環境、質檢環境中運行測試,也可以在無網絡的列車上用筆記本電腦運行測試。若是測試不能在任意環境中重複,你就總會有個解釋其失敗的接口。當環境條件不具有時,你也會沒法運行測試。
測試應該有布爾值輸出。不管是經過或失敗,你不該該查看日誌文件來確認測試是否經過。你不該該手工對比兩個不一樣文本文件來確認測試是否經過。若是測試不能自足驗證,對失敗的判斷就會變得依賴主觀,而運行測試也須要更長的手工操做時間。
測試應及時編寫。單元測試應該剛好在使其經過的生產代碼以前編寫。若是在編寫生產代碼以後編寫測試,你會發現生產代碼難以測試。你可能會認爲某些生產代碼自己難以測試。你可能不會去設計可測試的代碼。
對於衡量類的大小,這裏書中提出了一個不一樣的衡量方法:計算權責。我理解的意思就是,一個類承擔了太多的權責以後,這個類就算大了。
因此書中隨即提出了SRP - 單一權責原則(也叫單一職責原則)。
單一權責原則(SRP)認爲,類或模塊應有且只有一條加以修改的理由。該原則既給出了權責的定義,又是關於類的長度的指導方針。類只應有一個權責——只有一條修改的理由。
做者還提到了,系統應該由許多短小的類而不是少許巨大的類組成。每一個小類封裝一個權責,只有一個修改的緣由,並與少數其餘類一塊兒協同達成指望的系統行爲。
同時,做者提出了保持內聚性就會獲得許多短小的類。
類的高內聚的含義是:類的實體變量應儘量少,類中方法儘量多地使用到這些變量。(若是一個類中的每一個變量都被每一個方法所使用,則該類具備最大的內聚性)
在整潔的系統中,咱們對類加以組織,以下降修改的風險。
類應當對擴展開放,對修改封閉。經過子類化手段,類對添加新功能是開放的,並且能夠同時不觸及其餘類。
DIP認爲類應當依賴於抽象而不是依賴於具體細節。經過這種抽象隔離了系統之間的元素,使得系統每一個元素的理解變得更加容易,使用起來更加靈活、更加可複用。
系統構造與使用分開。
這裏我理解就是將一些對象實例的初始化和使用分離解耦,將構建實例的邏輯交給一個公共的模塊/類/框架來作。這裏做者也介紹了開發中常見的兩種方式,體現了這種思想:
後半章主要講的是AOP的思想和具體的框架實現。就是說將一些重複性、功能性的代碼(如:性能監視、日誌記錄、事務管理、安全檢查、緩存等)進行關注面切分,模塊化,成就了分散化管理和決策。最終的效果也顯而易見,減小了重複代碼,關注面的分離也使得設計、決策更加清晰容易。
這一節主要是講了四個簡單的設計規則(design rules),經過遵循這四個規則,你能夠編寫出很好的代碼,深刻了解代碼的結構和設計,繼而以一種更簡單的方式來學習掌握SRP和DIP之類的設計原則。
Four rules of Simple Design are of significant help increating well-designed software
全面測試並持續經過全部測試。遵循SRP的類,測試起來較爲簡單。測試編寫得越多,就越能持續走向編寫較易測試的代碼。因此,確保系統徹底可測試能幫助咱們建立更好的設計。
有了全面的測試保駕護航以後,咱們就有條件一步一步地去重構完善咱們的代碼,目的是爲了獲得「高內聚,低耦合」的系統。書中也提出了下面三條簡單的規則。
當你在重構時,按照SRP、代碼可讀性等規則遵照,是有可能建立出比原來更多的細小的類。但這不在本條的針對範圍以內。
這裏的儘可能減小,做者舉例了一種狀況,就是毫無心義的教條主義會致使編碼人員無心識的建立不少的類和方法。不知道你有沒有相似的經歷,我拿我親身體會舉個例子,我很難理解在某個項目中,對一個領域對象(如User),在構建對應的Service層和Dao層的時候,必定要爲每一個類建立接口,即便這些接口根本不可能有其餘的實現類。
「Objects are abstractions of processing. Threads are abstractions of schedule.」
—James O. Coplien
這一節做者討論了併發編程的需求和難點,而且給出了一些解決這些困難和編寫整潔併發代碼的建議。由於關於併發編程有更好的資料能夠學習,因此這裏我就簡單總結一下。
其餘未提到的章節,是我以爲相較來講非重點的章節。還有可能會有一些內容的遺漏,由於這本書中的精華,我以爲我還須要學習領會。
好書常讀常新,這本書就在個人工位上,我但願在經歷一段時間的工做實踐以後,再次打開這本書,我能有更多更新的一些感悟。
若是本文有幫助到你,但願能點個贊,這是對個人最大動力🤝🤝🤗🤗。