《代碼整潔之道》

第二章 有意義的命名

2.2 名副其實:變量、函數或者類的名稱要說明他們的目的。
2.3 避免誤導:避免使用關鍵字,避免使用0 o 1 l 等。
2.4 做有意義的區分:要區分名稱,就要以讀者能鑑別不同之處的方式來區分。
2.5 使用讀得出來的名稱:要使用恰當的英語詞,而不是傻乎乎的自造詞。
2.6 使用可搜索的名稱:避免使用單字母的名稱,或者直接使用數字。
2.7 避免使用編碼:帶編碼的名稱通常不便發音,容易打錯,更容易讓人混淆,而且給人增加了解碼的負擔。(比如:匈牙利命名法)
2.8 避免思維映射:
2.9 類名:類名應當是名詞或者名詞短語。類名不應當是動詞。
2.10 方法名:方法名應當是動詞或者動詞短語。
2.11 別扮可愛:名稱不能太耍寶。寧可明確,勿爲好玩。
2.12 每個概念對應一個詞:給每個抽象概念選一個詞,並且一以貫之。
2.13 別用雙關語:避免將同一單詞用於不同母的。同一術語用於不同概念,基本上就是雙關語。
2.14 使用解決方案領域名稱:只有程序員會讀你的代碼。
2.15 使用源自所涉問題領域的名稱:
2.16 添加有意義的語境:需要用良好命名的類、函數或者名稱空間來放置名稱,給讀者提供語境,如果沒有這麼做那就添加前綴說明吧。
2.17 不要添加沒用的語境:只要短名稱足夠清楚,就要比長名稱好。別給名稱添加不必要的語境。

第三章 函數

3.1 短小:函數的第一原則是要短小。第二條規則是還要更短小。(我認爲短小是沒錯,但是要在幹單一事情的前提下,把函數拆分,我覺得是不合理的)
3.2 只做一件事:函數應該做一件事。做好這件事。只做這一件事。
3.3 每個函數一個抽象層次:要確保函數只做一件事,函數中的語句都要在同一抽象層級上。
3.4 switch語句:switch天生就是幹N件事情。用多態實現switch我覺得使用看情況的,不能盲目的將switch轉換爲多態。
3.5 使用描述性的名稱:如果每個例程都讓你感到深合己意,那就是整潔代碼。
3.6 函數參數:函數參數要儘量少,如果函數參數多餘3個,則應該考慮創建一個新類了。參數起名要清晰的說明參數的意圖。
3.7 無副作用:副作用是一種謊言。函數承諾只做一件事,但還是會做其他被隱藏起來的事。
3.8 分隔指令與詢問:函數要麼做什麼,要麼回答什麼,但是二者不可兼得。函數應該修改某對象的狀態,或者返回某對象的信息。兩樣都幹常會導致混亂。
3.9 使用異常替代返回錯誤碼:使用異常代替錯誤碼,錯誤處理本身就是一件事。錯誤碼枚舉類型會成爲磁鐵需要所有需要處理錯誤的文件包含它。
3.10 別重複自己:重複是軟件中一切邪惡的根源。
3.11 結構化編程:每個函數中的買個代碼塊都應該有一個入口一個出口。

第四章 註釋 (別給糟糕的代碼加註釋---重新寫吧)

註釋的恰當用法是彌補我們在用代碼表達意圖時的失敗。
4.1 註釋不能美化糟糕的代碼:與其花時間寫註釋解釋你的代碼,還不如花時間重新整理你的代碼。
4.2 用代碼來闡述:儘量少寫註釋,用更形象的命名來取代註釋。
4.3 好註釋:有些註釋是必須的,也是有利的。

(1). 法律信息:爲程序提供法律註釋。
(2). 提供信息的註釋:用註釋提供基本信息。
(3). 對意圖的解釋:提供現有代碼後續的意圖。
(4). 闡釋:把晦澀難明的事情闡述清楚。
(5). 警示:用於警告其他程序員某種後果。
(6). TODO註釋:在代碼中放置工作列表。注意:要定期檢查 然後修護。
(7). 放大:註釋可以放大某種看來不合理之物的重要性。
(8). 公共API的DOC:對公共API做的DOC。

4.4 壞註釋:壞註釋是槽糕代碼的支撐或藉口,或是對錯誤決策的修正。

(1). 喃喃自語:不要對程序加無用的註釋,那樣只會自說自話。甚至讓人誤解代碼的真正意圖。
(2). 多餘的註釋:多餘的註釋使讀程序的難度更大。
(3). 誤導性註釋:誤導的註釋容易使使用你的代碼的人陷於調試的困境中。
(4). 循環性註釋:簡單的只闡述方法名,意圖明確的參數或者作者等信息的註釋,不需要總是放在每個函數前面。
(5). 日誌式註釋:每修改一次就在文件開始處記錄修改內容,修改日期等的日誌方式。在有版本控制軟件的今天這個已經是多餘。
(6). 廢話註釋:對於顯然之事喋喋不休。
(7). 可怕的廢話:代碼作者在複製代碼時把註釋也複製過來,但是結果代碼修改了,而註釋沒有修改。
(8). 能用函數或變量時就別用註釋:
(9). 位置標記:用來標記位置的特殊註釋,一定要清理。
(10). 括號後面的註釋:放在長循環,或者長if比括號後,指示是那個語句的結束括號。如果你想在右括號後加註釋,那你應該考慮下寫個短小的函數。
(11). 歸屬與署名:代碼版本控制軟件是這類註釋的最佳歸屬。
(12). 註釋掉的代碼:用版本控制軟件做歷史記錄。把已經不需要的代碼立即刪掉。
(13). HTML註釋:還有這種註釋嗎,天啊。
(14). 非本地信息:把註釋加到離目標代碼最近的地方。而不是加到遠離目標代碼的位置。否則目標代碼被修改時,註釋必然會被忘記修改。這是噩夢的開始。
(15). 信息過多:別在註釋中添加有趣的歷史性話題或者無關的細節描述。
(16). 不明顯的聯繫:註釋及其描述的代碼之間的聯繫應該是顯而易見。如果你不嫌棄麻煩要寫註釋,至少讓讀者能看着註釋和代碼,並且理解註釋所談何物。
(17). 函數頭:爲短函數選個好命,顯然要比函數前加註釋要好。
(18). 非公共代碼的DOC:顯然非公共代碼API不需要DOC。

第五章 格式

5.1 格式的目的:爲將來的修改減少阻力。
5.2 垂直格式:短文件比長文件好理解。

(1). 向報紙學習:名稱簡單一目瞭然;源文件頂部給出高層次概念和算法;細節應該從上到下逐次展開。
(2). 概念間垂直方向上的區隔:每個函數、每個功能塊之間用空行隔開。
(3). 垂直方向上的靠近:緊密相關的代碼應該互相靠近。
(4). 垂直距離:變量聲明應儘可能靠近使用位置。實體變量應該統一放在統一地方(C++統一放在類底部)。相關函數應該離得儘可能的靠近。概念相關的代碼應該放在一起。
(5). 垂直順序:一般使用自上而下的順序。函數調用盡量遵從。

5.3 橫向格式:每行字符個數應該遵從不需要拉動滾動條的原則。推薦120個字符。

(1). 水平方向上的區隔與靠近:使用空格將不相關的東西分隔。使用空格把相關的東西緊密聯繫。(厄,好彆扭的說法)
(2). 水平對齊:不要刻意去水平對齊,這樣只會讓讀者的注意力分散在不重要的東西上。
(3). 縮進:層級之間一定要縮進固定寬度。
(4). 空範圍:儘量不要寫只有一行的for 或者while,如果實在沒有辦法,那就把分號寫到下一行,讓讀者重視這裏。

5.4 團隊規則:如果團隊有規則則要遵從團隊規則。

第六章 對象和數據結構

6.1 數據抽象:取值函數和賦值函數並不是一個簡簡單單的函數那麼簡單,它代表了數據抽象以及數據封裝的思想。實現和應用是隔離的。
6.2 數據、對象的反對稱性:對象把數據隱藏於抽象之後,暴露操作數據的函數;數據結構暴露其數據,沒有提供有意義的函數。對象和數據結構之間的二分原理:過程式代碼便於在不改動即有數據結構的前提下添加新函數。面向對象代碼便於在不改動即有函數的前提下添加新類。也即:過程式代碼難以添加新數據結構,因爲必須修改所有函數。面向對象代碼難以添加新函數,因爲必須修改所有類。
6.3 得墨忒耳律(The Law Of Demeter):模塊不應瞭解他所操作對象的內部情形。也即方法不應調用由任何函數返回的對象的方法。

(1). 火車失事:不要直接對函數返回的對象調用方法。聲明一個對象將函數返回的對象賦值給它,然後再該對象的方法。
(2). 混雜:不要把對象和數據結構混在在一起使用。這樣你即得不到數據結構的好處也得不到對象的好處。
(3). 隱藏結構:

6.4 數據傳送對象:只有公共變量沒有函數的類叫做數據傳送對象。用於傳遞參數等。

 

第七章 錯誤處理

7.1 使用異常而非返回碼:返回錯誤碼需要立刻檢查錯誤碼,但是程序員往往忘記檢查,因此最好是使用異常。
7.2 先寫try catch finally語句:這樣可以在程序中定義一個範圍,而這個範圍中的錯誤可以被系統捕獲。即使你不做任何處理。
7.3 我們不可控異常:
7.4 給出異常發生的環境說明:在拋出異常的時候要提供詳細的發送說明。
7.5 依調用者需要定義異常類:
7.6 定義常規流程:
7.7 不要返回null值:在任何返回null值得地方都要進行判定,如果有一處沒有判定那代碼就將失控。
7.8 不要傳遞null值:傳遞null值意味着每一處傳遞的地方都要進行判定,如果有遺忘的地方那代碼同樣會失控。

 

第八章 邊界

8.1 使用第三方代碼:第三方代碼或多或少都會有些限制,要注意這些限制。有可能下一版本中就會有改變呢。
8.2 瀏覽和學習邊界:
8.3 這一條款的電子書頁缺失
8.4 學習性測試的好處不只是免費:
8.5 使用尚不存在的代碼:
8.6 整潔的邊界

第九章 單元測試

9.1 TDD三定律:在編寫不能通過的單元測試前,不可編寫生產代碼;只可編寫剛好無法通過的單元測試,不能編譯也算不能通過;只可編寫剛好足以通過當前失敗測試的生產代碼。
9.2 保持測試整潔:測試代碼要和生產代碼一樣整潔。測試代碼和生產代碼一樣的重要。
9.3 整潔的測試:測試代碼的要遵從的就是可讀性。比生產代碼更要可讀。
9.4 每個測試一個斷言:每個測試函數只測試一個概念。
9.5 F.I.R.S.T:快速(Fast) 測試應該夠快、 獨立(Independent) 測試之間應該相互獨立、可重複(Repeatable) 測試應該可在任何環境重複通過、自足驗證(Self-validating) 測試應該有布爾值輸出 、及時(Timely) 測試應及時編寫。

第十章 類

10.1 類的組織:次序先公用方法,保護方法,私有方法,共有數據,保護數據,私有數據。
10.2 類應該短小:類中的共有方法不應該過多。

(1). 單一職權原則:系統應該由許多短小的類而不是幾個龐大的類組成。每個小類封裝一個權責,每個小類只有一個修改的理由。
(2). 內聚:內聚性高意味着類中的方法和變量互相依賴,互相組合成一個整體。
(3). 保持內聚性就會得到許多短小的類:當類喪失內聚性的時候就拆分它。

10.3 爲了修改而組織:

第十一章 系統

11.1 如何建造一個城市:
11.2 將系統的構造和使用分開:構造和使用不是一樣的事情。

(1). 分解main:將全部的構造過程放到main函數中。
(2). 工廠:管理構造過程,把構造過程封裝到這裏。
(3). 依賴注入:分離構造與使用的強大機制。

11.3 擴容:
11.4 Java代理:
11.5 純Java AOP框架:
11.6 AspectJava的方面:
11.7 測試驅動系統架構:將架構按需從簡單演化到精細。沒有必要先做大設計(Big Design Up Front),BDUF甚至是有害的,它阻礙改進,因爲心理上抵制丟棄即成之事。也因爲框架上的選擇影響後續的設計思路。
11.8 優化決策:提前決策是一種預備知識不足的決策。
11.9 明智使用添加了可論證價值的標準:
11.10 系統需要領域特定語言:使用領域特定語言可以提高效率。

第十二章 迭進

12.1 通過迭代設計達到整潔目的:運行所有測試;不可重複;表達了程序員的意圖;儘可能減少類和方法的數量;遵循以上四條對良好設計有莫大幫助。
12.2 簡單設計規則1 運行所有測試:運行的測試越多,說明系統更健壯。
12.3 簡單設計規則2~4 重構:對有壞味道的代碼及早進行重構,有利於以後的維護。也會讓系統變的更明確。
12.4 不可重複:重複是擁有良好設計系統的打敵。重複意味着額外的工作,額外的風險以及不必要的複雜度。
12.5 表達力:代碼應當清晰表達作者的意圖。代碼寫的越清晰,維護人員花的時間就越少,從而減少缺陷,減少維護成本。其實維護人員最大可能就是自己,清晰代碼,是對自己以後工作的減負。
12.6 儘可能少的類和方法:在保持類和函數短小的情況下儘可能的減少類和函數的數量。

第十三章 併發編程

對象是過程的抽象,線程是調度的抽象。
13.1 爲什麼要併發:最大限度的使用CPU資源,提高工作效率(自己的觀點)。書中對併發的說法:併發會在性能和編寫一些額外代碼上增加一些開銷;正確的併發是複雜的,即便對於很簡單的問題也是如此;併發缺陷並非總能重現,所以常被看做偶發事件而忽略。併發常常需要對設計策略進行根本性修改。
13.2 挑戰:併發會引發一系列問題。死鎖,競爭,飢餓。。。等等。
13.3 併發防禦原則:

(1). 單一職權原則:分離併發相關代碼以及一般代碼。
(2). 推論--限制數據作用域:謹記數據封裝;嚴格限制對可能被共享的數據的訪問。
(3). 推論--使用數據副本:避免數據共享的好方法之一就是一開始就避免數據共享。
(4). 推論--線程應儘可能的獨立:嘗試將數據分解到可被獨立線程操作的獨立子集。

13.4 瞭解Java庫:
13.5 瞭解執行模型:根據併發應出的問題可以有如下模型

(1). 生產者-消費者模型:一個或幾個線程生產消息放入到共享隊列中,然後一個或幾個線程消費消息從共享隊列。共享隊列是一種限定資源。
(2). 讀者-作者模型:
(3). 宴席哲學家:

13.6 警惕同步方法之間的依賴:避免使用一個共享對象的多個方法。
13.7 保持同步區域微小:儘可能的減小同步區域。
13.8 很難編寫正確的關閉代碼:儘早考慮關閉問題,儘早另其工作正常。
13.9 測試線程代碼:編寫有潛力暴露問題的測試,在不同的編程配置、系統配置和負載條件下頻繁的運行。

(1). 將僞失敗看做可能的線程問題:不要將系統錯誤歸咎於偶發事件。
(2). 先使非線程代碼可工作:不要同時追蹤線程缺陷和非線程缺陷。確保代碼在線程之外可工作。
(3). 編寫可插拔的線程代碼:編寫可插拔的線程代碼,這樣就可以在不同的配置環境下運行。
(4). 編寫可調整的線程代碼:在編寫過程中要隨時可改動線程的數量以及自我調整。
(5). 編寫多於處理器數量的線程:系統在切換任務時發生一些事情,爲了促使任務切換的發生,運行多處理器核心數量的線程。頻繁的任務切換,可提高發現缺陷的機率。
(6). 在不同平臺上運行:儘早並經常地在所有目標平臺上運行代碼。
(7). 裝置試錯代碼:可以放置sleep、wait、yield、priority等操作,可以提高發現缺陷的機率。
(8). 硬編碼:找到合適的位置放置上面條款中的四個函數。
(9). 自動化:使用異動策略搜出錯誤。

 

第十四章 逐步改進

14.1 Args的實現:要想寫整潔的代碼,必須先寫髒代碼,然後整理它。多數新手剛開始就只寫能工作的代碼,然後繼續寫下面的功能,而那些能工作的代碼,僅僅保持在能工作而已。
14.2 Args草稿:簡單的系統通過多次的改動會變的龐大,雜亂。這時候你應該停下來,重構代碼。
14.3 字符串參數:

第十五章 JUnit內幕

第十六章 重構SerialDate

第十七章 味道與啓發

代碼的壞味道,與<<重構>>那本書中,有內容重疊。與前面的章節中的內容也有重疊。
17.1 註釋:

C1:不恰當的註釋。
C2:廢棄的註釋。
C3:冗餘的註釋。
C4:槽糕的註釋。
C5:註釋的代碼。

17.2 環境:

E1:需要多步才能實現的構建。
E2:需要多步才能做到的測試。

17.3 函數:

F1:過多的參數。
F2:輸出參數。
F3:標識參數。
F4:死函數。

17.4 一般性問題:

G1:一個源文件中存在多種語言。
G2:明顯的行爲未被實現:最小驚異原則(函數或者類應該實現其他程序員有理由期待的行爲)。
G3:不正確的邊界行爲:不要依賴直覺。
G4:忽視安全:切爾諾貝利核電站爆炸了,因爲電廠經理一條又一條的忽略了安全機制。
G5:重複。
G6:在錯誤的抽象層級上的代碼。
G7:基類依賴於派生類。
G8:信息過多。
G9:死代碼。
G10:垂直分隔。
G11:前後不一致。
G12:混淆視聽。
G13:人爲耦合:不要因爲方便把代碼隨便放置在別的類。
G14:特性依戀。
G15:選擇算子參數。
G16:晦澀的意圖。
G17:位置錯誤的權責。
G18:不恰當的靜態方法。
G19:使用解釋性變量。
G20:函數名稱應該表達其行爲。
G21:理解算法。
G22:把邏輯依賴改爲物理依賴。
G23:用多態替代if/else switch/case。
G24:遵循標準約定。
G25:用命名常量來代替魔術數。
G26:準確:對於代碼中的每一處都要做到準確。
G27:結構基於約定。
G28:封裝條件。
G29:避免否定性條件。
G30:函數只該做一件事。
G31:掩蔽時序耦合。
G32:別隨意:構建代碼需要理由,而且理由應該與代碼結構相契合。隨意的代碼別人想着改它,結構自始至終都一致的別人會去用它。
G33:封裝邊界條件:邊界條件的檢查應該封裝在一起,而不是散落在代碼各處。
G34:函數應該只在一個抽象層級上。
G35:在較高層級放置可配置數據。
G36:避免傳遞瀏覽:這條前面也有,這裏的這個名字起的很含糊。其實很簡單就是不要調用函數返回對象的函數。

17.5 Java:

17.6 名稱:

N1:採用描述性名稱。
N2:名稱應與抽象層級相符。
N3:儘可能使用標準命名法。
N4:無歧義的名稱。
N5:爲較大作用範圍選用較長的名字。
N6:避免編碼。
N7:名稱應該說明副作用。

17.7 測試:

T1:測試不足。
T2:使用覆蓋率工具。
T3:別略過小測試。
T4:被忽略的測試就是對不確定事情的疑問。
T5:測試邊界條件。
T6:全面測試相近缺陷。
T7:測試失敗的模式有啓發性。
T8:測試覆蓋率的模式有啓發性。
T9:測試應該快速。

完美之道,不在於無可增加,而在於無可減少。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章