SOLID:五大設計原則

★★★★★Draft

FromClean Architecture

📚 From the Books

SOLID:五大設計原則軟體架構

SOLID 是五條讓「中層」軟體結構容忍變更、易於理解、可被重用的原則。這裡的「中層」指的是模組與類別的層級,但 Uncle Bob 的關鍵洞見是:同一套原則從類別層級一路適用到元件與架構層級。它們的共同目標——建立能容忍變化、易於維護、可重用的軟體結構。

🧠 Core Ideas

TIP

SRP 最容易被誤解。它不是「函式只做一件事」(那是 Clean Code 的函式紀律),而是「模組只對一個角色負責」。書中的 Employee 類別同時服務 CFO(calculatePay)、COO(reportHours)、CTO(save)三個角色——三個不同的人會為三個不同的理由來改它,這才是違反 SRP。

⚖️ 違反 SRP 的兩種真實災難

當服務不同角色的程式碼被混在一起,麻煩不是抽象的「不夠優雅」,而是會賠掉真金白銀。

意外重複與合併衝突
  • 意外重複(Accidental Duplication)。假設 calculatePayreportHours 都呼叫同一個私有的 regularHours()。CFO 要求改工時計算方式,開發者改了 regularHours() 滿足 CFO,卻意外影響了 COO 的報表數據——而 COO 對此一無所知。書中直言:這類 Bug 曾導致數百萬美元的損失。

  • 合併衝突(Merge)。DBA 為了資料庫變更在改 save(),同時財務人員為了新薪資政策在改 calculatePay()——兩個人為了完全不同的理由,改同一個原始檔。版本控制的合併衝突與過程中的出錯,都是把不同角色綁在一起的直接後果。

  • 解法是分離:把資料(EmployeeData)與行為(PayCalculatorHourReporterEmployeeSaver)拆開,讓它們互不認識;再用 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

✍️ My Notes

No notes yet — jot your takeaways or Q&A here.

📖 Further Reading