「相對於任何宏偉景願,對細節的關注甚至是更爲關鍵的專業性基礎。首先,開發者經過小型實踐得到可用於大型實踐的技能和信用度。其次,宏偉建築中最細小的部分,好比關不緊的門,有點兒沒有鋪平的地板,甚至是凌亂的桌面,都會將整個大局的魅力毀滅殆盡。這就是整潔代碼之所繫」----沒有比書中的這段話更能說明這本書的意義了。java
![](http://static.javashuo.com/static/loading.gif)
《代碼整潔之道》是第1期書山有路活動選出的讀本。相對於記住那些如何寫出整潔代碼的那些法則,養成保持代碼整潔、提升代碼質量的習慣和思惟更爲重要。全書大體分爲三個部分,第一部分1-10章都是介紹如函數、類、命名、單元測試等保持整潔的建議。第二部分11-13章從系統設計的層面提倡用AOP、IOC等方式保持整潔,或者合適的時候使用併發編程。第三部分14-17章以及後面的附錄,做者以JAVA源碼(全書都是以JAVA代碼示例)來實際講解如何保持整潔。程序員
later equals never編程
咱們都曾經瞟一眼本身親手形成的混亂,決定棄之不顧,走向新的一天。咱們都曾經看到本身的爛程序竟然能運行,而後斷言能運行的爛程序總比什麼都沒有強,咱們都曾經說過有朝一日再回頭清理。固然,在那些日子裏。咱們都沒有聽過布朗法則:later equals never 稍後等於永不.設計模式
ps:看到這句話確實有點慚愧。印象最深的感受就是,咱們去看一兩年前本身的代碼。那是寫的什麼玩意,真的是本身都看不下去。要讓我改,我情願再實現一個。因此時刻保持好的習慣是多麼重要。不要想着之後再解決。就像領導說,這個事情之後再考慮,而後就沒有而後了。緩存
第一章 整潔代碼 安全
怎樣是整潔的代碼?
Bjarne Stroustrup(C++發明者) 說:
「我喜歡優雅和高效的代碼,代碼邏輯應當直接了當,叫缺陷難以隱藏;儘可能減小依賴關係,使之便於維護;依據某種分層戰略完善錯誤處理代碼;性能調至最優,免得引誘別人作沒有必要的優化,搞出一堆混亂來,整潔的代碼之作好一件事。」
Ron Jeffries 對整潔代碼的理解:
1.能經過全部的測試。
2.沒有重複代碼。
3.體現系統中的所有設計理念。
4.包含儘可能少的實體、好比類、方法、函數等。
「在以上諸項中,我最在乎的是代碼重複。若是一段代碼重複出現,就表示某種想法未在代碼中獲得良好的提現。我會盡力去找出到底那是什偶麼,而後在盡力的更清晰的表達出來。」
PS:總結下就是整潔的代碼1.職責明確,沒有多餘,2.減小依賴,便於維護。3.高效。
第二章 有意義的命名
1.名副其實。 提及來很簡單。選個好名字須要花時間,但省下的時間比花掉的多。注意命名,一旦有好的命名,就換掉舊的。
int d;// 消失的時間,以日計。
int elapsedTimeInDays;
PS:Ctrl+R+R蠻好用的。實際中的狀況要比我貼出來的複雜。咱們定義不一樣的變量,可以看到名字就知道是什麼意思,這是最基本的要求了。網絡
2.避免誤導。好比不是List類型,就不要用個accountList來命名,這樣造成誤導。數據結構
3.作有意的區分。
Public static void copyChars(char a1[],char a2[]){
for(int i=0;i<a1.length;i++)
{
a2[i]=a1[i];
} }
若是參數名稱改成source和destination ,這個函數就會像樣不少。廢話都是冗餘的,Variable一詞 永遠不該當出如今變量名中。Table一詞永遠不該當出如今表名中。NameString 會比 Name好嗎,難道Name 會是一個浮點數不成?若有一個Customer的類,有又一個CustomerObject的類。是否是就凌亂了。併發
4.使用便於搜索的的名稱編程語言
單個字母或者數字常量是很難在一大堆文章中找出來。好比字母e,它是英文中最經常使用的字母。長名勝於短名稱,搜獲得的名稱勝於自編的名稱。 竊覺得單字母的名稱僅用於短方法中的本地變量。名稱長短應與其做用域大小相對應。
5.類名應該是名詞或短語,像Customer,Account,避免使用Manager,Processor,Data或者Info這樣的類名。類名不該當是動詞。方法名應該是動詞或動詞短語,如postPayment ,deletePage或Save,屬性訪問、修改和斷言應該根據其值來命名,並加上get,set,is這些前綴。
6.別扮可愛,耍寶,好比誰知道HolyHandGrenada 函數是幹什麼的,沒錯這個名字挺伶俐,可是不過DeleteItems或許是更好的名字。
7.每一個概念對應一個詞。而且一以貫之。
在一堆代碼中有Controller,又有manager,driver。就會使人困惑。好比DeviceManager和Protal-Controller之間又什麼本質區別?
第三章 函數
1.函數的第一規則是要短小,第二條規則是還要更短小。
2.函數應該作一件事。作好這件事。只作這一件事。
3.儘可能少的函數參數。有兩個參數的函數要比一元函數的難懂。若是須要三個或者三個以上的參數應該封裝成類了。
4.不要重複本身。
PS:若是一段相同的代碼出現了兩次,你是否是以爲本身改作些什麼了。
第四章 註釋
註釋的恰當用法是彌補咱們在用代碼表達意圖時遭遇的失敗。做者認爲註釋是一種失敗,咱們總沒法找到不用註釋就能表達自個人方法,因此總要有註釋,這並不值得慶賀。寫註釋的常見動機之一是糟糕代碼的存在。
帶有少許註釋的整潔而有表達力的代碼,要比帶有大量註釋的零碎而複雜的代碼像樣的多。與其花時間編寫解釋你搞出的糟糕的代碼註釋,不如花時間清潔那堆糟糕的代碼。
PS:這段話看起來可能有些過激。咱們確實能夠經過好的編碼習慣減小沒必要要的註釋。不過如今自動生成文檔的技術都是從代碼的註釋中提取的。若是是這種狀況,上司確定是要求你寫完備的註釋的。
好註釋:
1. 法律信息。有時,公司代碼規範要求編寫與法律有關的註釋。例如版權和著做申明。
2.提供信息的註釋。
// returen an instance of the Responder being tested
protected abstract Responder responderInstance();
不過做者認爲 將函數名 從新命名爲 responderBeingTested 註釋就是多餘的。
3.對意圖的解釋。 有時註釋不只提供了有關實現的有用信息,並且還提供了某個決定後面的意圖。
4.闡釋。 有時註釋把某種晦澀難明的參數或返回值的意義翻譯爲某種可讀形式。也會是有用的。特別是參數或者返回值是某個標準庫的一部分,或者你不能修改代碼,那幫助闡釋其含義的代碼就會有用,例如:
assertTrue(bb.compareTo(ba)==1);//bb>aa
assertTrue(a.compareTo(b)==-1);//a<b
直接看方法可能不明確,但有註釋就明白多了。我看這2,3,4都是一個意思。就是說明是幹嗎的。
5.警示,告訴別人要注意這個方法之類的。
6.放大。有的代碼可能看着有點多餘,但編碼者當時是有他本身的考慮,這個時候須要註釋下這個代碼的重要性。避免後面被優化掉。
第五章 格式
縱向格式:
1. 函數與函數之間留空行。
2.變量聲明:變量聲明應該儘量靠近其使用位置。由於函數很短,本地變量應該在函數的頂部出現。
3.實體變量 應該在內的頂部,至關於咱們的field 字段,會被使用的多。
4.相關函數,若是某個函數調用另一個,就應該把他們放在一塊兒,並且調用者應該儘量放在被調用者的上面。這樣這個程序就會天然有序。(以前我喜歡把private的方法 放到一塊兒。固然這確實沒有什麼實際的意義)
5.「相關概念的代碼放在一塊兒。相關性越強,好比一個大功能邏輯靠在一塊兒。」 (更多的時候我喜歡用 region 來收起來。)
橫向格式:
1.一行的長度,做者建議是上限是120個字符
PS 平時咱們都是按照本身的屏幕大小來決定,固然太長了,本身也不便閱讀,又不是壓縮的js文件
2.賦值語句兩端留空。
3.不在函數名和左括號間加空格。由於函數與其參數密切相關。
4.縮進。源文件是一種繼承結構,而不是一種大綱結構,繼承結構中的每一層級都圈出一個範圍, 也就是代碼塊,其中有聲明語句和執行語句。要體現這種繼承結構,就要對源代碼進行縮進處理。但有時候咱們會把if語句,while循環,或小函數寫成一行,但這樣沒有層級的概念,不便閱讀,仍是縮進的好。
第六章 對象和數據結構
1.過程式代碼(函數編程)便於在不改動既有數據結構的前提下添加新函數,面向對象代碼便於在不改動既有函數的前提下添加新類。反過來說也說的通,過程式代碼難以添加新的數據結構,由於必須修改全部函數,面向對象代碼難以添加新函數,由於必須修改全部類。因此在設計的時候要分析好是之後是要添加新函數仍是要添加新的數據結構。
2.德墨忒爾律:模塊不該該瞭解它所操做對象內部情形。好比C的方法f只能調用如下對象的方法。
- C
- 由f建立的對象
- 做爲參數傳遞給f的對象
- C的實體變量持有的變量
var outpath=cxt.getOptions().getScart().getAbsolutePath();
這個代碼就違反了上面的德墨忒爾律,調用了返回值的方法。這樣就是暴露了內部結構。
第七章 異常處理
1.try代碼就像是事務,catch代碼塊將程序維持在一種持續狀態。在編寫可能拋出異常的代碼時,最好先寫出try-catch-finally 語句。
2.根據須要定義異常類。對錯誤分類的方式有多種,能夠依據來源,是組件仍是其餘地方,或者依據類型,是設備錯誤仍是網絡錯誤。不過在咱們定義異常類的時候,最重要的考慮是如何捕獲它們。
3.別返回null值。程序中不斷的看到檢測null值的代碼,一處漏掉檢測就可能會失控。返回Null,做者認爲這種代碼很糟糕。建議拋出異常 或者返回特定對象(默認值)。更早的發現問題。同理,也應該避免傳遞Null值給其餘的方法。
PS:在大多數的編程語言中,沒有良好的方法能對付由調用者意外傳入的null值。咱們發佈產品應該有容錯的機制,程序不能輕易的就崩掉,有異常應該及時記錄下來或給出提示。
第八章 邊界
有時候咱們在使用第三方程序包或者開源代碼的時候,或者依靠公司其餘團隊的代碼,咱們都得乾淨利落的的整合進本身的代碼中。這一章就是介紹保持保持軟件邊界整潔的實踐手段和技巧。
1.對第三方進行學習性測試,當第三方程序包發佈了新的版本,咱們能夠容許學習性測試,看看程序包的行爲有沒有發生改變。
2.使用尚不存在的代碼,有時候咱們的第三方,尚未開發好API,但又不能影響到咱們的開發進度,因此咱們先能夠定義好本身想要的接口。若是第三方ok了,咱們再對接起來。
3.經過接口管理第三方邊界,可使用ADApter模式將個人接口轉換爲第三方提供的接口。這個是要注意,第三方的代碼和本身的代碼混合太多,這樣不便管理。
第九章 單元測試
敏捷和TDD運動鼓舞了許多程序員編寫自動化單元測試,單元測試是確保代碼中的每一個犄角旮旯都如咱們所願的工做。
TDD三定律
- 除非這能讓失敗的單元測試經過,不然不容許去編寫任何的生產代碼。
- 只容許編寫恰好可以致使失敗的單元測試。 (編譯失敗也屬於一種失敗)
- 只容許編寫恰好可以致使一個失敗的單元測試經過的產品代碼。
PS:什麼是生產代碼,這裏有點迷惑。
測試代碼和生產代碼同樣重要,它可不是二等公民,它須要被思考、被設計和北照料。它該像生產代碼同樣保持整潔。單元測試讓你的代碼可擴展,可維護,可複用。緣由很簡單,有了測試,你就不擔憂對代碼的修改,沒有單元測試,每次修改可能帶來缺陷,一個測試,一個斷言。一個測試,對應一個概念。咱們不想要超長的測試函數。
測試還應遵照如下5條規則。
1.快速 測試應該能快速運行,太慢了你就不會頻繁的運行,就不會盡早的發現問題。
2.獨立。測試應該相互獨立,某個測試不該該爲下個測試設定條件。當測試相互依賴,一個沒經過致使一連串的測試失敗,使問題診斷變的困難。
3.可重複。測試應該能夠在任何環境中重複經過。
4.自足驗證 測試應該有布爾值輸出,不管經過或失敗,不該該是查看日誌文件去確認
5.及時。單元測試應該剛好在使其經過的生產代碼以前編寫。
第十章 類
1.類應該短小
2.單一權責原則(SRP):類或模塊應有且只有一條加以修改的理由。系統應該有許多短小的類而不是巨大的類組成。
PS:每一個達到必定規模的系統都會包括大量邏輯和複雜性。管理這種複雜性的首要目標就是加以組織,以便開發者在哪兒能找到東西,反之,
擁有巨大、多目的的類的系統,老是讓咱們在目前並不須要瞭解的一大堆東西中艱難的跋涉。
3.內聚:若是一個類中的每一個變量都被每一個方法所使用,則該類具備最大的內聚性。內聚性高,意味着類中的方法和變量相互依賴,相互結合成一個邏輯總體。
4.爲了修改而組織。開放閉合原則(OCP):類應當對擴展開放,對修改封閉。咱們能夠藉助接口和抽象類來隔離這些細節帶來的影響。
第十一章:系統
將系統的構造和使用分開:構造和使用是不同的過程。
PS:修建一棟大樓的時候,起重機和升降機在外面,工人們穿着安全服在忙碌。當大樓建設完成,建築物變得整潔,覆蓋着玻璃幕牆和漂亮的漆色。在其中工做的人,看完徹底不一樣的景象。軟件也是如此,將關注的方面分離。
1.工廠,有時候應用程序須要肯定什麼時候建立對象,咱們可使用抽象工廠模式。將構造的細節隔離於應用程序以外。
2.依賴注入(DI/IOC)。在依賴管理情景中,對象不該該負責實例化對自身的依賴,反之,它應該將這份權責移交給其餘有權利的機制,從而實現控制的反轉。
PS 如今的依賴注入組件比較多了,Autofac,Ninject等。
3.擴容:「一開始就作對的系統」純屬神話,反之,咱們應該只實現今天的用戶的需求。而後重構,明天再擴容系統,實現新用戶的需求。這就是迭代和增量敏捷的精髓所在。 就像城市不斷的再拆掉,再建設。
4.面向切面編程。AOP中,被稱爲方面(aspect)的模塊構造指明瞭系統中哪些點的行爲會以某種一致的方式被修改,從而支持某種特定的場景。這種說明是用某種簡潔的聲明(Attribute)或編程機制來實現的。
PS:MVC的Filter是個很好的AOP,能夠從權限驗證,方法進入前,方法進入後,返回結果前,返回結果後等這幾個橫切面進行編程。更好的組織代碼。第十,十一章講的設計只是一少部分。更多的可能要去參考專門講設計模式之類的書。
第十二章 迭進
1.簡單設計規則 1:運行全部測試。
緊耦合的代碼難以編寫測試。一樣編寫測試越多,就會越遵循DIP之類的原則,使用依賴注入,接口和抽象等工具儘量減小耦合。如此一來設計就會有長足進步。遵循有關編寫測試並持續運行測試的、明確的規則,系統就會更貼近OO低耦合度、高內聚的目標。
2.簡單設計規則2 重構:
在重構過程當中,能夠應用有關優秀軟件設計的一切知識,提高內聚性,下降耦合度。換句話說:消除重複,保證表達力,儘量的減小類和方法的數量。
3.不可重複。重複是良好設計系統的大敵。它表明着額外的工做、額外的風險和額外沒必要要的複雜度。重複有多種表現。雷同的代碼行是一種。另外的好比:
int size();
bool isEmpty();
這兩個方法能夠分別實現,但能夠在isEmpty中使用size消除重複。
bool isEmpty(){
return size()==0;
}
不可是從代碼行的角度,也要從功能上消除重複。
第十三章: 併發編程
併發是一種解耦策略,它幫助咱們把作什麼(目的)和什麼時候(時機)作分解開。在單線程應用中,目的與時機緊密耦合,不少時候只要查看堆棧追蹤便可判定應用程序的狀態。而解耦目的與時機能明顯地改進應用程序的吞吐量和結構。從結構的角度看,應用程序看起來更像是許多臺協同工做的計算機,而不是一個大循環。單線程程序許多時間花在等待Web套接字I/O結束上面。
- 併發會在性能和編寫額外代碼上增長一些開銷。
- 正確的併發是複雜的,即便對於簡單的問題也是如此。
- 併發缺陷並不是總能重現,因此常被看作偶發事件而忽略,而未被當作真的缺陷看待。
- 併發經常須要對設計策略的根本性修改。
一些基礎定義:
在併發編程中用到的幾種執行模型。
1)生產者-消費者模型
一個或多個生產者線程建立某些工做,並置於緩存或者隊列中。一個或者多個消費者線程從隊列中獲取並完成這些工做。生產者和消費者之間的隊列是一種限定資源。
2)讀者-做者模型。
當存在一個主要爲讀者線程提供信息源,但只是偶爾被做者線程更新的共享資源,吞吐量就會是個問題。增長吞吐量,會致使線程飢餓和過期信息的積累。協調讀者線程不去讀取正在更新的信息,而做者線程傾向於長期鎖定讀者線程。
3)宴席哲學家。
許多企業級應用中會存在進程競爭資源的情形,若是沒有用心設計,這種競爭會遭遇死鎖,活鎖,吞吐量和效率低等問題。
PS:這裏對併發的講解還不是那麼的清晰,要掌握怎麼正確使用併發,本身仍是須要去專門看看這方面的書。
小結:書十三章以後的部分是一些java源碼的優化過程的講解,我不太懂java,這裏先略過。本書最有價值的地方在於讓咱們程序員要有些整潔代碼的習慣。從細微的變量命令,到函數、類的設計、以及整個系統的構造。不能忽略每一道工序。壞的代碼就像沼澤,會讓人越陷越深,很難改動,因此咱們從一開始就要寫整潔的代碼。而至於設計模式或併發編程,從其餘的書籍
學習
更全面。這本書知足不了咱們的需求。
PS:書山有路活動是讀書羣的朋友共同選出來一塊兒讀的一本書。《代碼整潔之道》是第一期。我是讀書人,這本書一共讀了七天。天天大概一個多小時。可是今天整理筆記,基本上全書又過了一遍。筆記內容也是依據我本身的判斷。若是你想得到全面的瞭解,仍是要請看原書。咱們第二期正在讀的書籍是《失控》,歡迎有興趣的朋友加入。qq羣452450927