Skip to main content
Select a menu in the customizer

Clean code: 無瑕的程式碼 – 書摘心得(二)

clean code
程式與程式的邊界,第三方函式庫總是希望功能越強,但是總要有個邊界,才不會無限延伸。進而反被控制

Clean code: 無暇的程式碼 – 書摘心得(一)

第八章 邊界

大部分的開發情況,都會使用第三方的函式庫,在整齊的邊界上,如何漂亮的整進來。這是個大齋問

8.1 學習式測試

這件事本身並沒有多花太多工,因為不管怎某樣,我們都要學會這api的使用方式,而寫這些測試可以幫助獲得相關知識,是一個簡單又無不良影響的方式,確認api有沒有問題,且增進暸解。撰寫這樣的測試可以保證每次函式庫升級時,確保不會帶來新的風險

不管你是不是想靠測試學習api,還是必須要有一套『與生產程式碼採用相同方式的邊界測試』 來支援以往整潔的介面,如果沒有邊界測試,來減輕升級整合所造成的風險,停留在舊版程式的時間,可能比解bug 還要久
(p.133)

第九章 單元測試

寫測試程式讓我們免於寫錯程式的恐懼

9.1 TDD 三大法則

1.在撰寫一個單元測試前(測試失敗的單元測試),不可撰寫任何產品程式。
2.只撰寫剛好無法通過的單元測試,不能編譯也算無法通過
3.只撰寫剛好能通過當前測試失敗的產品程式
(p.139)

按照上述的方式來寫程式,最後測試程式數量理應跟產品程式差不多,那某老樣子要回到如何管理這一堆程式碼?
如果在撰寫測試程式時沒有保持『整潔』,那最後維護測試程式的時間可能就會比產品程式還要麻煩,最慘最慘就是回到原點,沒有測試的境界,結論很簡單『測試程式跟產品程式一樣重要』。

這樣聽起來測試似乎變成一種沈重的工作,儘管如此,他帶來的好處還是遠大於『修改產品程式帶來的失誤』的恐懼。在這基礎上,你將可以肆無忌譚的修改程式,達到不斷精練的效果。(p.140)

9.2 整潔的測試

三個重點『可讀性,可讀性,還是可讀性
(p.141)

9.2.1 特定領域的測試語言

直接使用特定的api,可能一開始會造成理解上的困難,那這時候寫測試程式可以加上許多細節的提點,幫助未來的使用者加速理解。
(p.144)

9.2.1 一個測試一個斷言(assert)

斷言數量越少,可以更精確地鎖定要測試的對象。那某可以更加精鍊成『一個測試一個概念
(p.148)

整潔的測試原則 F.I.R.S.T
Fast: 要能快速的被運行,緩慢只會讓你增加不耐煩。
Independent: 測試程式不該相互依賴。一個測試程式不應該被建立在另一個測試程式執行完的結果。
Repeatable: 應該要可以再任何環境重複執行,不要出現『我機子上可以啊』這種無意義的解釋。
Self-Validating: 測試程式應該輸出boolean。你不該手動比較兩個檔案才知道有沒有通過測試,或是看log才能知道。
Timely: 單元測試要在恰好能使其通過的產品程式之前不久撰寫,太久會忘記當初產品程式的設計理念。
(p.149)

整潔的單元測試可以完整個寫一大本,這只是個開頭

第十章 類別

簡短再簡短

根據Java的慣例,一個類別的開頭為一連串變數做開頭
1.公用靜態常數
2.私有靜態常數
3.私有實體變數
4.公有實體變數

封裝上並不要求絕對私有,為了讓測試能方便測試,開放成protected是能接受的

在上述前提上
竟量的保持簡短,不應該出現數千行的萬能類別,命名上也要避免模糊地帶
(p.152)

10.1 單一職責原則

主張一個類別應該只有一項任務或值職責『一個修改的理由

一個類別裡可能有多個函式或變數,此時我們在意的並非行數,而是職責的數量,最完美的就是只有一個職責,維持這件事情並不容易,大多數人還是維持在『讓軟體能運作』而非『讓軟體整潔』,因此三不五時回去檢視過去寫的程式是必要的。
(p.155)

一個優秀的系統是由許多小類別組成,而不是由少數大類別組成,每個小類別『封裝單一的職責』『維持單一的修改理由』『由竟量少的類別組合完成系統的要求』
(p.156)

10. 2 凝聚性

類別應該只有少量的實體變數,類別的每個函式都應該操作一個或更多的這類別的變數,這代表者這個函式更加凝聚於該類別。
(不過實際上不太可能,這方面還是簡短的完成功能就好。)

為了保持凝聚性,就會達到上一小節的單一職責原則,形成許多量的類別。
(p.157)

10.3 隔離修改

在修改類別以達到凝聚性時,勢必會拆解大型的類別,這是一個不斷的修改過程,因此最好達到『隔離修改』的方式,利用『具體類別』和『抽象類別』的分別,如果修改在具體類別則有風險,因此建築在抽象類別則可以達到隔離修改的效果。

這樣的重構可以更加確立以下兩個原則
開放閉合原則,類別應該要對擴充有開放性,對修改有封閉性
相依性反向原則,類別應該要相依於抽象概念,而不是相依在具體細節
(p.167)

第十一章 系統

這章可不是開玩笑,完全看不懂

第十二章 羽化

透過羽化設計來達到簡潔,或許有個準則可以讓你檢視程式碼是否整潔

12.1 簡單設計準則

1. 執行完所有的測試:為了讓系統達到能夠被測試的需求,所有函式或類別的設計都會朝向『單一職責原則』並以最小耦合設計完成
2. 沒有重複的部份:多餘的程式碼只會造成多餘的麻煩,重構的要件之一
3. 表達程式設計師的本意:前幾章的總結,簡單且易懂
4. 最小化類別和方法的數量:同上,維持強大表達力

(p.190)

第十三章 平行化

『物件是處理過程的抽象化,執行緒是排程的抽象化』
這是個大問題,足以另外出一本書來談談『作啥事?』和『何時做』

13.1 迷失及誤解

平行化是件難事,請三思避免這個危機

1. 平行化可以改善效能:有時候是可以效能但僅限於有很多等待時間可以被多執行緒或多處理氣氛想的情況,兩種情況都不簡單。
2. 撰寫平行化程式並不需要修改原有的設計:把『何時做』和『作何事』去耦合絕對是大工程。
3. 當利用web或EJB 等容器來處理平行化,會產生的問題不是那某重要:搞清楚狀況會比較好喔

(p.199)

中肯版
1. 平行化會帶來某些額外負擔:效能處理及去耦合的程式碼
2. 正確的平行化是複雜的:即便是簡單的問題
3. 平行化的錯誤通常不容易出現:很多情況組合在一起才會reproduce
4. 平行化會對設計策略上進行整體的修改

(p.200)

13.2  平行化的防禦原則

單一職責原則:保持你的平行化程式碼與其他程式碼有清楚的劃分。
1. 限制資料的視野:利用synchronized,保護共享物件的臨界區域。把資料封裝的原則謹記在心,並嚴格現職共享資料的存取次數。
2. 使用資料的副本:多執行緒執行時存取時,可以盡可能讓共享物件複製多分,保持唯獨。最後在單一執行緒時和併起來(CopyOnWriteArrayList() )
3. 執行緒盡可能的獨立執行:資料劃分清楚,平均分攤,讓重疊部份盡可能的少

(p.201)

13.3 理解你的函式庫

1. 使用函式庫提供的安全執行緒集合(java.util.concurrent)
2. 使用executor框架來執行不相關的工作
3. 減少使用non blocking 的解法

(p.203)

13.4 測試執行緒程式碼

不要把偶而發生的事情給忽略掉。
1. 把偽造的失敗當作是淺在的執行緒問題。
2. 先讓非執行緒的程式碼通過。
3. 在各種環境下都可以穩定運作。
4. 讓執行緒的數量比處理器還多。
5. 讓執行緒程式碼是可以調校的。
7. 調整程式碼,讓他故意失敗。

(p.208)

第十四章 持續的精煉

不介紹,看書吧,指令列參數剖析器的範例展示

第十五章 JUnit 的內部結構

不介紹,看書吧,JUnit 的範例展示

第十六章 重構 SerialDate

不介紹,看書吧,SerialDate 的範例展示

第十七章 程式碼的氣味和啟發

列出許多重構上的清單,很多前面幾章節都有提到,整理文
topic: comment , environment , function , general , names , test

這一張節我只寫下我注意的幾條,沒寫的不是不重要,可能是我看不懂,或是前面有提過

g1: 同份文件存在多種語言
g5: 重複的程式碼
g7: 基底類別相依逾期衍生類別 base classes depending on their derivatives(被jar class 綁住)
g9: 被遺棄的程式碼 (DEAD CODE)
g13: 人為地耦合:不相依的程式碼,不需要耦合在一起,例如一般的enum不應該被榜定在特定的類別
g15: 選擇型參數:加個boolean arg ,很醜,可以看前幾章
g18: 不適當的靜態宣告
g20: 函式名稱要說到做到
g23: 用多型取代 if/else or switch/case
g30: 函式應該只做一件事
g36: 避免傳遞性導覽 a.getb().getc() 這是很不相關的連結

j2: 不要繼承常數:這個讓人找資料找到想哭啊,傳來的類別要到處翻找。
j3: 常數和列舉:這個我覺得有爭議,書中大推 enum

感謝收看,這本書到這已經到一段落,剩下的就是不斷的在實戰中練習(持續的精練),並定期回來複習

如果有任何違反著作權的情事發生,請主動告知,小弟會快速下架

下一篇是
The Clean coder: 無瑕的程式碼 番外篇 – 書摘心得