SOLID 是五條讓「中層」軟體結構容忍變更、易於理解、可被重用的原則。這裡的「中層」指的是模組與類別的層級,但 Uncle Bob 的關鍵洞見是:同一套原則從類別層級一路適用到元件與架構層級。它們的共同目標——建立能容忍變化、易於維護、可重用的軟體結構。
🧠 Core Ideas
- SRP(單一職責):一個模組應只有一個「被改變的理由」,精確說是「只對唯一角色(Actor)負責」。多數人誤以為它是「只做一件事」,但那是重構層次;架構層次談的是「為誰而寫」。
- OCP(開放封閉):軟體實體應「對擴展開放、對修改封閉」。由 Bertrand Meyer 於 1988 年提出——需求變動時,理想是「加新程式碼」而非「改舊程式碼」。
- LSP(里氏替換):由 Barbara Liskov 於 1988 年提出——子型別必須能無痛替換父型別而不改變程式的正確行為。「看來像鴨子、叫來像鴨子,但需要電池才能運作」就違反了 LSP。
- ISP(介面隔離):不該強迫客戶端依賴它用不到的方法。胖介面(Fat Interface)會把不相關的客戶捲入無謂的重新編譯與變更。
- DIP(依賴反轉):靈活系統的原始碼依賴只應指向抽象,不指向具體。因為抽象介面穩定、具體實作易變——「依賴穩定的,避開易變的」。
- 五者相互支撐:SRP 定義邊界該畫在哪、DIP 提供反轉依賴的機制,是整潔架構得以成立的兩根支柱。
TIP
SRP 最容易被誤解。它不是「函式只做一件事」(那是 Clean Code 的函式紀律),而是「模組只對一個角色負責」。書中的 Employee 類別同時服務 CFO(calculatePay)、COO(reportHours)、CTO(save)三個角色——三個不同的人會為三個不同的理由來改它,這才是違反 SRP。
⚖️ 違反 SRP 的兩種真實災難
當服務不同角色的程式碼被混在一起,麻煩不是抽象的「不夠優雅」,而是會賠掉真金白銀。
意外重複與合併衝突
-
意外重複(Accidental Duplication)。假設
calculatePay和reportHours都呼叫同一個私有的regularHours()。CFO 要求改工時計算方式,開發者改了regularHours()滿足 CFO,卻意外影響了 COO 的報表數據——而 COO 對此一無所知。書中直言:這類 Bug 曾導致數百萬美元的損失。 -
合併衝突(Merge)。DBA 為了資料庫變更在改
save(),同時財務人員為了新薪資政策在改calculatePay()——兩個人為了完全不同的理由,改同一個原始檔。版本控制的合併衝突與過程中的出錯,都是把不同角色綁在一起的直接後果。 -
解法是分離:把資料(
EmployeeData)與行為(PayCalculator、HourReporter、EmployeeSaver)拆開,讓它們互不認識;再用 Facade 收攏呼叫端的方便性。
🖼️ LSP 違反的代價是「整個架構被污染」
Uncle Bob 用一個計程車調度案例,把 LSP 從「繼承語法問題」提升到「架構契約問題」。系統透過 REST API 呼叫各家車行,約定 URI 帶 destination;但 Acme 車行的工程師擅自把它縮寫成 dest。
為了支援這一家違規業者,系統核心被迫加入 if (uri.startsWith("acme.com")) 的特例判斷。後果是連鎖的:具體公司名 "acme" 入侵了核心程式碼,每新增一家違規業者就要改一次;若 Acme 併購 Purple Taxi,是否又要加 if "purple"?以公司名做分支還埋下難察覺的錯誤與安全隱患。合理的架構師不會容許這種寫法——正確做法是把特例抽到「設定資料庫」用查表 + 格式化驅動。但代價很清楚:只因一家服務商不遵守契約,整個系統就被迫多出一套機制來對抗不一致。這說明 LSP 的違反從不只是「程式碼變醜」,而是逼系統付出架構層級的補丁成本。
IMPORTANT
SOLID 的價值在於它是「跨層級」的——同一條原則從類別、元件一路貫穿到系統架構。SRP 是元件層 CCP 的種子、ISP 是 CRP 的種子、DIP 是整條依賴規則的引擎。理解這五條原則,等於拿到了從微觀類別設計通往宏觀依賴管理的鑰匙。
🔑 Takeaways
- SRP 不是「只做一件事」,而是「只對一個角色負責」;混合角色會導致意外重複與合併衝突。
- OCP 靠擴充而非修改因應變化;LSP 要求子型別能無痛替換,違反的代價是架構被污染。
- ISP 別逼客戶依賴用不到的方法;DIP 依賴穩定的抽象、避開易變的具體。
- 五原則相互支撐,從類別層一路適用到元件與架構層。
- 延伸:DIP 反轉依賴後,整個系統就服從一條「依賴指向內層」的規則,見 依賴規則與邊界:細節指向策略。
No notes yet — jot your takeaways or Q&A here.