SRP 是 SOLID 原則中最易被誤解的。多數人以為它是「每個模組應只做一件事(Do one thing)」。
但這是重構(Refactoring)層次,而非架構層次的 SRP。
Uncle Bob 給出 SRP 在架構上的真正定義:
「一個模組應只有一個理由可以被改變。」
(A module should have one, and only one, reason to change.)
什麼叫做「改變的理由」?精確說法是:「模組應只對唯一角色(Actor)負責。」
一、違反 SRP 的症狀#
當我們將具備不同角色(Actor)的程式碼混在一起時,就會發生悲劇。
書中舉了一個經典 Employee 類別為例:
這個 Employee 類別違反了 SRP,因它同時包含了三個不同角色的邏輯:
| 方法 | 負責角色 | 職責 |
|---|---|---|
calculatePay() | 財務部門(CFO) | 計算薪水 |
reportHours() | 人資部門(COO) | 報告工時 |
save() | 技術部門(CTO) | 資料庫儲存 |

Figure 7.1: The Employee class
這會導致兩個嚴重症狀:
意外重複 (Accidental Duplication)#
這最致命。假設 calculatePay 和 reportHours 都依賴一個共用私有演算法 regularHours() 來計算正常工時。

Figure 7.2: Shared algorithm
- 情境: CFO 決定修改工時的計算方式
- 災難: 開發者修改
regularHours()滿足了 CFO,
但卻意外影響了 COO 的reportHours()報表數據(而 COO 對此一無所知) - 後果: 系統帶著 Bug 上線,導致數百萬美元的損失
合併衝突 (Merge)#
- 情境: DBA 正在修改
save()(為了適應資料庫變更),
同時財務人員正修改calculatePay()(為了新的薪資政策) - 災難: 兩個不同開發者為了不同理由,修改同個原始檔(Source file)
- 後果: 版本控制的合併衝突,且合併過程中極易出錯
二、解決方案:分離與 Facade#
解決 SRP 違反的策略:分離(Separate)。
分開服務不同角色的程式碼,不把它們放在同個類別中。
1. 分離資料與行為#
最簡單的做法是將資料結構(Data Structure)與函數(Functions)分開。
- 建立一個單純的
EmployeeData資料結構(無邏輯) - 建立三個獨立類別:
PayCalculator、HourReporter、EmployeeSaver - 這些類別只包含各自所需原始碼,互不認識,避免意外重複

Figure 7.3: The three classes do not know about each other
2. Facade 模式 (外觀模式)#
雖然分開了三個類別解決了問題,但對呼叫端(Client)來說變麻煩了(需實例化三個類別)。
這時可用 Facade Pattern。
- 做法: 建立一個
EmployeeFacade類別 - 功能: 幾乎沒有邏輯,只負責實例化小類別,並 委託(Delegate) 工作給它們

Figure 7.4: The Facade pattern

Figure 7.5: Employee class as Facade
三、結論#
SRP 告訴我們,架構設計不僅僅是功能的堆砌,更是職責的劃分。
- 識別角色: 寫程式碼前,先問自己:「這段程式碼是為誰而寫?」
- 隔離變更: 確保某個角色改變需求時,不會影響到其他無關角色