一、迭進
Kent Beck關于簡單設計的四條原則:
- 運行所有測試
- 不可重復
- 表達程序猿的意圖
- 盡可能減少類和方法的數量
- 以上顧著按其重要程度排序
1.1 簡單設計規則1:運行所有測試
設計的首要目的是設計出如預期一般工作的系統。
全面測試并持續通過所有測試的系統,就是可測試系統。看似淺顯,但卻重要。不可測試的系統不可驗證。不可驗證的系統不可部署。緊耦合的代碼難以編寫測試。同樣,測試編寫越多,就越會遵循DIP之類的規則。
1.2 簡單設計規則2~4:重構
有了測試,就能保持代碼和類的整潔,方法就是遞增式地重構代碼。完成修改,運行測試,保證沒有破壞任何東西。所以,<b>測試消除了清除代碼就會破壞代碼的恐懼</b>。
重構的過程中,可以應用有關優秀軟件設計的一切知識。提升內聚性,降低耦合度,切分關注切面,模塊化系統關注面,縮小函數和類的尺寸,選用更好的名稱。即消除重復,保證表現力,盡可能較少類和方法數量。
1.3不可重復
重復是擁有良好設計系統的大敵。Repeat Code代表extra work,意味著extra risk。大量重復可以用模板方法來消除。
1.4表現力
這需要嘗試,讓后來者易于閱讀代碼。
1.5盡可能減少類和方法
這是為了防止教條主義,片面追求設計原則,從而導致過度切分。例如給每一個類編寫接口,所有行為都切分為數據類和行為類。但是這是優先級最低的一條。
二、并發編程
對象是過程的抽象,線程是調度的抽象。——James O Coplien
并發是一種解耦策略。它幫助我們把做什么(目的)和何時(時機)做分解開。這種解耦能有效改進應用程序的吞吐量和結構。
并發編程很難,往往不細心,你會搞出不知道什么鬼的惡心東西出來。看看下面的迷思和誤解:
- 并發總能改變性能
并發有時候能改變性能,但一般在多個線程或處理器之間能風向大量等待時間的時候管用。 - 編寫并發程序無須修改設計
這是完全bullshit。目的和時機的解耦代表著往往對系統結構產生巨大的影響。 - 在采用Web或EJB容器的時候,理解并發問題其實不重要。
實際上你最好理解并發在干什么。
總結一下:并發有額外的開銷;正確的并發是復雜的;并發缺陷并不總能重現,所以被當做是“基因突變”;并發往往需要對設計策略的根本性的修改。
2.1并發防御原則
2.1.1單一權責原則
SRP:方法、類、組件應當只有一個修改理由。
- 并發相關代碼有自己的開發、修改和調優生命周期
- 并發相關代碼有自己要對付的挑戰,和并發相關代碼不同
- 并發編程錯誤不可控,可能會導致game over、
建議:分離相關代碼和其他代碼。
2.1.2推論:限制數據作用域
如我們所見,連個線程修改共享對象的統一字段時。采用臨界區域形式保護(synchronize)。限制臨界區數目,臨界區數目越多:
- 忘記保護一個或多個臨界區——破壞了共享數據的代碼。
- 花力氣保護一切都受到保護。(遵循DRY原則:Don't repeat yourself)
- 很難找到錯誤源。
2.1.3推論:使用數據副本和線程盡可能獨立
避免共享數據的方法就是一開始就避免共享數據。
讓每個線程在自己的世界里存在,不與其他線程論短長。
2.2執行模型
基礎定義:互斥、線程饑餓、死鎖、活鎖。
執行模型:
- 生產者-消費者:生產者和消費者之間的隊列是一種限定資源。
- 讀者-作者模型:當存在一個主要圍讀者線程提供資源的共享資源,其偶爾才被作者進行寫。協調吞吐量很重要。
- 宴席哲學家:獲得所有資源,執行,否則等待。
2.3警惕同步方法的依賴和保持同步區域微小
同步方法之間的依賴會導致并發代碼中的狡猾缺陷。Java有synchronize關鍵字,保護單個方法,然而,如果再同一共享類中有多個同步方法,系統就可能寫得不太正確了。
<b>建議:避免使用一個共享對象的多個方法。</b>
三種對寫代碼保護方法:
- 基于客戶端鎖定
- 基于服務端鎖定
- 適配服務器
盡量減少同步區。
2.4很難編寫正確的關閉代碼和測試線程代碼
關閉的建議:盡早考慮關閉問題,盡早令其工作。
盡管測試無法證明代碼的正確性,但測試可以降低風險。
測試的建議:編寫有潛力暴露問題的測試,在不同的環境下頻繁運行。
- 將偽失敗看做是線程的問題:不要講系統錯誤歸咎于“基因突變”。
- 先使非先線程代碼可工作:不要同時最終非線程缺陷和線程缺陷。
- 編寫可插拔的線程代碼:在不同配置環境下運行。
- 編寫可調整的線程代碼。
- 運行多余處理器數量的線程。
- 在不同平臺運行。
- 調整代碼并強迫錯誤發生。