跳轉到主要內容
類別: 原則
類型: 軟體開發原則
來源: Robert C. Martin,《敏捷軟體開發》,2003
別名: SRP,單一職責原則
快速回答 — 單一職責原則(Single Responsibility Principle,SRP)是一種軟體設計準則,主張每個模組、類別或函數應該只有一個變更理由。該原則由Robert C. Martin於2003年在其著作《敏捷軟體開發》中提出,是SOLID原則的組成部分,為建立可維護、可測試的靈活軟體系統奠定了基礎。

什麼是單一職責原則?

單一職責原則是一種設計規則,主張每個軟體元件應該只有一個主要職責或目的。當一個類別或模組承擔多個職責時,對其中一個職責的修改可能會無意中影響其他職責,從而產生難以維護和測試的脆弱系統。
「一個類別應該只有一個變更的理由。」 — Robert C. Martin
該原則的名稱源於「一次只做一件事」的理念。在實踐中,這意味著將不同的關注點分離到不同的模組中。例如,處理使用者驗證的類別不應該同時格式化資料顯示——這是兩個不同的職責:安全和展示。當顯示格式發生變化時,你不需要修改驗證邏輯,反之亦然。

單一職責原則的三個層次

  • 入門: 識別程式碼中不同的「變更理由」。如果需求變化會迫使你出於多個目的修改某個類別,那麼該類別可能違反了SRP。
  • 實踐: 在函數層面同樣應用SRP。一個函數應該做一件事:把輸入轉換為輸出。如果你在描述一個函數時發現自己要說「而且」,就應該考慮拆分它。
  • 進階: 使用「變更軸線」測試。問自己:「如果X領域的需求發生變化,我需要修改這個類別嗎?」如果多個領域都回答「是」,那麼這個類別就承擔了多個職責。

起源

單一職責原則由Robert C. Martin(人稱「Uncle Bob」)在其2003年出版的《敏捷軟體開發:原則、模式與實踐》一書中推廣開來。Martin將SRP作為後來被稱為SOLID原則的一部分引入——這是一套物件導向編程的五大設計指南。 這一概念本身早於Martin的正式表述。開發者早就認識到,將不相關的功能組合在一起會造成維護上的噩夢。然而,Martin將這一原則提煉為一條可檢驗的規則:計算一個類別可能需要變更的理由數量。如果超過一個,就違反了SRP。 該原則隨著更廣泛的敏捷運動而獲得關注,敏捷運動強調迭代開發和適應變化需求的能力。在敏捷背景下,易於修改的程式碼是有價值的——SRP透過減少不相關關注點之間的耦合直接實現了這一點。

核心要點

1

提升可維護性

當每個元件只有一個職責時,變更都被限制在局部。當需求演進時,開發者只需要理解和修改程式碼庫的一個區域。
2

增強可測試性

具有單一職責的元件更容易單獨測試。你可以在不考虑無關邏輯或副作用的情況下驗證行為。
3

降低耦合度

分離職責建立了鬆耦合的模組。這使系統更加靈活,允許元件獨立地重用或替換。
4

便於重構

更小、更專注的類別更容易安全地進行重構。當職責被清晰劃分時,破坏無關功能的風險就會降到最低。

應用場景

類別設計

拆分處理多個關注點的類別。例如,將一個負責驗證、儲存和顯示使用者資料的User類別拆分為三個專注的類別。

函數提取

將大型函數拆分為更小的函數。每個函數應該執行一個轉換或操作。

模組組織

將程式碼組織成具有單一、明確目的的模組。報表模組應該只處理報表,而不處理資料檢索或格式化。

API設計

設計每個端點都服務於特定目的的API。避免建立「萬能」端點來處理多個用例。

經典案例

2003年,一家金融服務公司對其交易平台進行了重大改造。原始程式碼庫有一個名為「Trade」的類別,處理驗證、持久化、通知、報表和風險計算。由於緊耦合,任何一個方面的變更——比如新增的通知渠道——都需要對整個系統進行測試。 團隊使用SRP進行了重構,將Trade類別拆分為五個專注的類別:TradeValidator、TradeRepository、TradeNotifier、TradeReporter和RiskCalculator。每個類別只有一個職責,可以獨立修改。 結果非常顯著。由於回歸測試要求而產生的原本需要數天才能修復的缺陷,現在可以在幾小時內解決。新增的通知渠道只需要修改TradeNotifier類別即可。在接下來的一年中,團隊報告新功能的部署時間減少了40%。 這個案例說明了SRP的實踐價值:雖然分離職責的前期成本需要思考,但長期在可維護性和開發速度方面獲得的收益是 substantial 的。

邊界與失效場景

SRP最常見的誤用是過度拆分。建立「每個方法一個類別」反而適得其反,因為會產生一個難以導航的系統。該原則的核心是關於內聚性——將相關事物分組——而不是任意最小化類別的大小。 另一個失敗模式是錯誤識別職責。「變更理由」有時是主觀的。一個同時處理信用卡和銀行轉帳的PaymentProcessor可能看起來像是兩個職責,但如果兩者的業務邏輯緊密交織,拆分它們可能會增加複雜性。 邊界條件:當由於耦合導致的變更成本超過管理更多元件的成本時,才應用SRP。從內聚的設計開始,當注意到一個類別因多個不相關的原因需要變更時,再向SRP方向重構。

常見誤區

該原則並不是要盡可能減少程式碼行數。一個有500行但完美處理一個職責的類別是完全可以的。SRP關注的是內聚性,而不是大小。
雖然類別是最常見的應用,但SRP適用於所有層面:函數、模組、服務,甚至整個應用程式都應該有單一、明確的目的。
一些開發者避免使用SRP,因為他們擔心建立太多檔案。然而,現代IDE和程式碼組織實踐使得導航許多專注的檔案比管理複雜的多職責類別更容易。

相關概念

單一職責原則是一個設計原則家族的成員,這些原則共同促進軟體的可維護性:

介面隔離原則

客戶端不應該被迫依賴它們不使用的介面。ISP透過確保介面的專注性來擴展SRP。

開閉原則

軟體實體應該對擴展開放,對修改關閉。SRP透過隔離變更來支援OCP。

KISS原則

保持簡單。具有清晰職責的簡單程式碼更容易理解和維護。

一句話總結

當你發現一個類別可能因多個理由需要變更時——將其拆分為專注的元件,每個元件只有一個明確的目的。