~~~《名傢名著》03 V.S. 《無瑕的程式碼》03~~~
小記者︰能說說你對《無瑕的程式碼──敏捷完整版》的讀後心得嗎?
工程師︰自從讀瞭這本《敏捷完整版》之後,我再也不怕麵對那些慣老闆、慣客戶瞭。而且客戶滿意度、專案完成度都一百分呢!
這本書是《無瑕的程式碼》係列書的第三冊,也是《名傢名著》係列書的第三冊。主題是「敏捷開發」,而重點仍舊是迴歸到「如何撰寫齣好的程式碼」。
什麼是「敏捷開發(Agile Development)」呢?簡單來說,它是軟體開發的一套方法,特點是隻要透過這套方法,就能使你的專案更敏捷。
我們為何非得要讓專案變得敏捷呢?原因無他,就是因為我們有慣老闆、還有慣客戶。也就是說,對於現今的市場環境而言,專案不夠敏捷是不行的。這一點,相信所有的軟體工程師都無法否認吧!
可是你可能會反駁說,各行各業都有慣老闆和慣客戶啊(至少在颱灣是這樣),為什麼軟體業就要一套特彆的方式來應付他們呢?這就是要迴歸到一個最根本的問題,「什麼是軟體?」,或者更精確地說「什麼是軟體設計?」,而這個問題和所有的軟體工程師(或程式設計師)習習相關,因為這是工作的本質。
各式各樣的工程有著所謂的程序,例如橋樑工程師會先進行結構分析,他們會建立電腦模型並進行模擬,接著他們會建立比例模型,並在風洞中或用其他一些方法進行測試。當這些程序都完成瞭,纔會將設計圖交給橋樑的建造工人去建造齣真實的橋樑。
以上是橋樑工程的開發程序,那麼軟體開發的程序呢?在很久很久以前(真的是很久很久以前瞭),軟體開發也發展齣瞭所謂的程序,也就是瀑布型開發程序。在瀑布型開發中,係統分析師會依照需求與規畫,畫齣所謂軟體的設計圖(例如UML圖),然後由「程序員」根據這些圖去寫齣程式碼,最後建置(build)成可使用的軟體。
依照瀑布型開發程序開發齣來的軟體,客戶隻能選擇要用,還是不要用。不要用的話,是否有其他選擇?如果沒有,那麼客戶即便不滿意,也就隻能將就著用(隻是邊用邊罵而已)。當然,這是指套裝軟體的開發而言。
用一個例子來做比方,數十年前,颱灣隻有國道一號的日子,一位民眾想要開車從彰化到新竹,就隻能有一個選擇,即便他不滿意苗栗那段高爬坡會摺損車輛壽命,他也彆無選擇。但當國道三號建造完畢後,他就有瞭第二個選擇,因此他會選擇他喜歡的國道來行使。建造國道的總經費是昂貴的(無論是時間還是金錢),但最貴的部分是在於建造部分,而非設計部份。所以國道並不多。競爭者很少。但這種商業模式在軟體業是行不通的。
若用早期的瀑布型開發程序來對比於國道建設,真正的建造部分,其實就是軟體建置(build)的部分,這部分隻要一颱電腦,一個編譯器,一個連結器,還有一點點的時間就完成瞭。所以代價是極低的。或許有人會說,不對,建造的部分應該也要包含按照UML圖去Coding的人工與時間成本。所以這部分的代價應該也是昂貴的。
這種說法錶麵上看似閤理,但有多少程式碼是完全依照UML圖編寫的呢?在撰寫程式碼的過程中是否會修改原有的UML設計呢?早期這類情況並不嚴重,但晚期因為客戶的挑剔,這種情況早就屢見不鮮,甚至任何軟體工程師在開發專案時,心中早有預期會齣現需求發生變化的情況。
國道的建造工人是無權修改設計圖的,他隻能「按圖施工」。而程序員卻去修改瞭設計圖,這將使得設計圖無法作為最終産品的設計文件。因此,在這種情況下,最終産品的設計文件其實隻有一份是準確的,這份文件就是「程式碼」。同時,在這種情況下,程序員應該已經不再隻是「程序員」或「碼農」瞭,因為他參與瞭設計,換句話說,他應該稱之為程式「設計師」或軟體「工程師」。(在敏捷開發中,並不隻有那些繪製UML圖的纔叫做設計人員,正確地說,繪製UML圖的人常常也是負責寫程式的人)。
好的,如果你已經承認「寫程式」也算是「設計」的一環,那麼軟體建置(build)的成本(也就是軟體的建造成本,而非設計成本),應該是無庸置疑的低廉瞭。這也就是為什麼,客戶說,那邊改成XXX顔色,可以嗎?你會很乾脆地迴答,當然沒問題,然後五分鍾內就給客戶看改完之後的結果。想一想,如果要改的是一整段國道護欄的顔色,相信沒有客戶敢做這樣要求,因為他們能預期到,這會花很多很多的錢。
所以說,建造軟體的花費是很少的,大多數的錢都是花費在「設計」上的。但對於其他工程就不一樣瞭,設計花費的錢相對於建造花費的錢來說,低廉瞭許多。
也就是軟體的這種特殊性,導緻瞭客戶(更有可能的是上司)常常想要東改改、西改改,需求常常在變化。在現今這個快速變化的世界裏,慣客戶與慣老闆們為瞭競爭優勢(他們心中的競爭優勢),提齣需求的變化根本是傢常便飯。
在確定瞭「需求會變化」、甚至是「會頻繁地變化」這個軟體工程師一定得麵對的事實後,軟體工程師該怎麼辦呢?有一群大師級的軟體工程師,開始發明瞭一係列因應的對策,包含設計模式、極限程式設計、測試驅動開發等等的技藝,還總結瞭一些物件導嚮的設計原則。這些都有助於應付變化。最終,這些人集閤起來成立瞭一個「敏捷聯盟」,取名為敏捷(Agile),意思是軟體開發者及軟體本身應該如何敏捷地應付需求的變化,當中牽涉到的範圍極廣,從成員的組織到程式碼的組織都必須敏捷起來,這是門現代軟體設計的顯學,國外大廠早已採用多年。
Robert C. Martin(Bob大叔)是敏捷聯盟的創始成員之一,也是當中付諸行動並且有所成效的成員之一。他擁有極具說服力的文筆與口纔。在這些年中,不斷齣書、演講、作為顧問實際前往開發現場指導,並自創「Clean」一詞,其著作還曾獲得Jolt大奬,《Clean Code》一書也成為Amazon該類彆最暢銷的著作,這些都對於敏捷開發的推廣有著極重要的貢獻。
根據《Clean Code》內文的說法,《Clean Code》可說是本書的前傳,而本書是完整說明如何實踐敏捷的書籍。如果您也喜歡Bob大叔的著作,如果你也是Clean派的弟子,或者你想實際體驗敏捷開發,那麼你一定不能錯過這本書。
本書的寫作風格是循序漸進,由淺入深的,作者會先提齣一個問題,然後分析問題,接著實作它,然後是檢討它,展現齣初次實作時的錯誤與失策,接著就展示如何透過作者所主張的技術來解決這些問題。這是一本非常講究實務的實踐書籍。此外,本書主要使用的是C#程式碼,這是由Bob大叔的兒子Micah Martin根據C#與.NET平颱的特性重新改寫Jolt得奬著作而來的,改寫幅度包含所有的程式碼與內文,並採用瞭更容易理解的案例來詳述敏捷開發。如果你平常使用的是其他語言,也不必在意,因為傳播的介質不重要,傳授的內容纔是本書的價值所在。
對於一些技術細節,本書果真是大師級的作品,原創性極高,在UML章節中,Bob大叔示範瞭他如何使用UML(果真和一般人不太一樣),還示範瞭如何使用UML纔能幫助你而非是製造混亂的來源。對於設計模式而言,除瞭GoF的知名設計模式之外,Bob大叔還在本書中提到幾個他自己常用的設計模式,有些可以視為GoF 23個設計模式的變形,有些則不是,但重點是這些模式都非常好用,可以應用在不同的應用場閤,同時Bob大叔也釐清瞭,某些模式為何不該在哪些場閤中使用,他是以效益來看待這件事的,而這也是本書的最大特色:務實。
本書贊譽 這也許是第一本把敏捷方法、模式和最新的軟體開發基本原則完美結閤在一起的圖書。當Bob Martin發言時,我們最好洗耳恭聽。
John Vlissides ──《設計模式》作者
這本書中充滿瞭對於軟體開發的真知灼見。不管你是想成為一個敏捷開發人員,還是想提升自己的技能,本書都同樣有用。我一直在期盼著這本書,它沒有令我失望。
Erich Gamma ── JUnit之父,《設計模式》作者
我期待這本書已經很久瞭,關於如何去掌握我們的行業技能,本書作者擁有非常豐富的實際經驗可以傳授。
Martin Fowler ── 軟體開發大師,《重構》作者
前幾天,我找到瞭記有我對Bob大叔第一印象的備忘錄。上麵寫著'優秀的物件思維'。你手中的這本書就是能讓你受益終生的'優秀的物件思維'。
Kent Beck ── 軟體開發大師,JUnit之父,設計模式先驅
讀過無瑕的程式碼,一定要再讀「敏捷完整篇」,否則就是您的損失,它會解答您所有的疑惑。
《博碩文化》、《名傢名著》 總編輯 ── 陳錦輝
軟件構建的藝術與科學:係統性思維與高效能實踐 本書導覽 本書旨在為緻力於軟件工程深度實踐的專業人士提供一套係統化的知識框架與實戰指南。我們不再局限於單一語言或框架的錶麵操作,而是深入探討軟件係統的核心設計哲學、演進規律以及確保長期可維護性和高性能的構建策略。 本書聚焦於構建“健壯、可擴展、易於理解”的軟件。它不側重於特定框架的快速入門技巧,而是著眼於那些跨越技術棧、經受時間考驗的設計原則與模式。通過對軟件復雜性本質的剖析,讀者將學會如何管理大型係統的熵增,確保代碼庫在持續迭代中保持清晰的結構和高效的性能。 --- 第一部分:復雜性管理的基石——結構化思維 軟件的本質是管理信息和流程的復雜性。本部分將從根本上重塑讀者對代碼結構的認知,超越簡單的功能實現,轉嚮對係統整體形態的掌控。 1.1 抽象的層次與邊界的定義 軟件設計中的首要挑戰是如何有效地劃分職責。我們將探討不同粒度的抽象級彆,從函數到模塊,再到整個服務架構。重點在於如何識彆清晰的“邊界”——這些邊界定義瞭係統中不同組件之間的契約和依賴關係。 關注點分離 (Separation of Concerns, SoC) 的精細化應用: 不僅在模塊層麵,更深入到方法和數據結構層麵,確保每個單元隻負責一項明確的任務。我們將分析“職責分散”的常見陷阱,例如一個類同時處理業務邏輯、數據持久化和用戶界麵交互的情況,並提供重構策略。 隱式依賴的顯性化: 探討如何在不使用特定設計模式名稱的情況下,通過良好的結構設計來消除隱式的、難以追蹤的耦閤。這包括對“時間耦閤”和“數據流耦閤”的識彆與解耦技術。 1.2 狀態管理的哲學 在任何復雜的係統中,狀態是導緻不確定性和並發問題的核心根源。本書將係統地審視不同類型狀態的特性和管理策略。 可變性與不可變性 (Mutability vs. Immutability): 深入探討在不同場景下(如高性能計算、並發處理、配置管理)選擇不可變數據結構帶來的優勢和性能權衡。我們將展示如何通過函數式思維來最小化副作用。 狀態傳播與曆史記錄: 討論如何設計能夠清晰追溯狀態變更路徑的係統,這對於調試、審計和實現時間旅行(Time Travel Debugging)至關重要的機製。我們將分析事件溯源(Event Sourcing)的潛在應用場景,即使在傳統麵嚮對象環境中,也能藉鑒其核心思想。 1.3 對稱性與非對稱性在設計中的角色 良好的設計往往具有某種內在的“對稱性”,即對相似問題的處理方式應保持一緻。 一緻性原則的應用: 如何確保不同模塊對同一概念的理解和操作方式保持同步。我們將研究如何利用編程語言特性(如接口、泛型)來強製執行這種一緻性,避免齣現“同名異義”的混亂。 何時引入非對稱性: 識彆那些確實需要特殊處理的“邊緣情況”或“性能瓶頸點”,並探討如何將這些非對稱設計隔離在受控的邊界內,以防汙染核心邏輯。 --- 第二部分:演進式設計與重構的藝術 軟件的生命周期在於持續的演化。本書的第二部分專注於如何在不中斷現有服務的情況下,安全、有效地改進和重構代碼庫。 2.1 適應性架構:應對未知的變化 優秀的架構不是預先固定的藍圖,而是能夠適應未來需求變化的骨架。 鬆耦閤的度量與實踐: 介紹衡量耦閤度和內聚度的高級指標,超越簡單的類間調用數量。重點在於理解“信息傳遞的密度”對耦閤度的影響。 分層架構的動態演進: 探討傳統三層或MVC架構在微服務時代可能遇到的挑戰。我們不推崇盲目地采用最新架構風格,而是強調如何識彆當前架構的瓶頸,並進行局部、漸進式的重構,而非推倒重來。例如,如何安全地將一個緊密耦閤的內部組件轉化為一個獨立的、通過消息或API通信的服務。 2.2 安全重構的藝術:最小化風險 重構的阻力往往來自於對引入新錯誤的恐懼。本部分提供瞭確保重構安全性的方法論。 契約驅動的重構 (Contract-Driven Refactoring): 強調在修改內部實現前,必須先用測試來固化外部契約。我們將詳細討論如何構建“隔離層”或“適配器”來保護外部消費者,使其不受內部重構的影響。 “僵屍代碼”的識彆與清除: 探討如何係統性地識彆那些雖然存在但從未使用或已過時的代碼路徑。這需要依賴精確的覆蓋率分析和靜態分析工具,以及一種組織文化,鼓勵清除“安全地刪除”的舊邏輯。 2.3 優化性能的深層考量 性能優化不應是事後的補救,而應融入設計之初的考量。 算法復雜度與實際運行時間的平衡: 分析在現代多核、高緩存係統中,理論上的 $O(N^2)$ 算法在數據量較小時可能優於復雜的 $O(N log N)$ 算法的原因。這要求設計者具備對底層硬件模型的基本理解。 延遲與吞吐量的權衡: 探討在設計並發機製時,如何根據業務需求(例如,需要快速響應的實時交易係統 vs. 後颱批處理係統)來調整鎖的粒度、數據結構的布局和異步處理的深度。 --- 第三部分:構建高質量交付物的工程實踐 本部分聚焦於如何將設計理論轉化為可交付、可信賴的最終産品,強調自動化、質量保證和持續集成的重要性。 3.1 測試的真正價值:設計而非驗證 本書將測試視為“設計行為的副産品”,而非事後的驗證步驟。 依賴注入 (Dependency Injection) 的純粹作用: 不僅僅是“為瞭測試方便”,而是通過強製分離構造時配置和運行時代替物,來構建高內聚、低耦閤的組件。我們將探討更精細的“構造函數注入”與“屬性注入”的適用場景。 集成測試的邊界設定: 如何在單元測試的快速反饋和端到端測試的真實性之間找到最佳平衡點。討論如何使用“模擬(Mocking)”和“存根(Stubbing)”來精確隔離和測試特定行為,避免過度依賴外部係統。 3.2 可讀性超越簡潔性 代碼的長期維護成本遠高於編寫成本。本書強調“為後來的自己”和“為團隊新成員”編寫代碼的策略。 命名與意圖的對齊: 深入探討如何通過精確的命名(變量、函數、類)來編碼係統的意圖,減少閱讀代碼時進行心智轉換的開銷。 注釋的“真僞”: 分析何時注釋是必要的“為什麼”(Why),何時注釋隻是重復瞭代碼的“是什麼”(What)。推崇“自解釋性代碼”的極限,以及在復雜領域特定語言(DSL)中注釋的有效用法。 3.3 工具鏈與自動化部署的集成 將優秀的工程實踐固化到自動化流程中,確保每一次提交都遵循既定的質量標準。 靜態分析的進階使用: 不僅使用 Linter 檢查語法錯誤,更利用高級靜態分析工具來識彆潛在的運行時錯誤、未使用的資源和不一緻的模式應用。 構建流程中的質量門禁: 如何在持續集成(CI)管道中嵌入性能測試、安全掃描和代碼質量度量,確保隻有滿足預設閾值的代碼纔能進入部署階段。 本書為讀者提供瞭一套成熟的、經過反復驗證的軟件構建思維模型,旨在培養能夠設計和維護十年以上生命周期的復雜係統的頂尖工程師。它要求讀者願意深入理解底層原理,並對構建“優雅而強大”的軟件懷有不懈的追求。