單一職責(zé)原則(Single Responsibility Principle,SRP)是 SOLID 五大原則中的第一條,由 Robert C. Martin(“Uncle Bob”)提出。它是軟件設(shè)計(jì)中最基礎(chǔ)、也最容易被誤用的原則。和它的名字不同,它并不是“一個(gè)類(lèi)只做一件事”那么簡(jiǎn)單,而是:
一個(gè)類(lèi)應(yīng)該只有一個(gè)引起其變化的原因。
A class should have only one reason to change.
SRP 專(zhuān)注于“變化來(lái)源”。它要求開(kāi)發(fā)者將軟件中不同變化的方向分離,使每個(gè)類(lèi)(或模塊)對(duì)一種變化負(fù)責(zé)。
如果一個(gè)類(lèi)承擔(dān)了多種不同類(lèi)型的職責(zé),那么任何一種職責(zé)發(fā)生變化時(shí),都可能迫使類(lèi)被修改,最終導(dǎo)致代碼變得脆弱、龐大、難以維護(hù)。
一、為什么需要單一職責(zé)原則?
軟件之所以會(huì)難維護(hù),很大程度上是因?yàn)椤岸鄠€(gè)變化糾纏在一起”。SRP 通過(guò)“解耦變化來(lái)源”來(lái)提高可維護(hù)性。
(1)多個(gè)職責(zé)會(huì)讓類(lèi)變得不穩(wěn)定
例如,當(dāng)日志、業(yè)務(wù)邏輯、數(shù)據(jù)存儲(chǔ)、驗(yàn)證規(guī)則混在一個(gè)類(lèi)里時(shí),其中任何一個(gè)需求變更都會(huì)讓整個(gè)類(lèi)被修改。
(2)修改一個(gè)功能容易破壞另一個(gè)功能
當(dāng)職責(zé)混合時(shí),影響范圍無(wú)法隔離。一個(gè)修改可能引發(fā)連鎖錯(cuò)誤。
(3)類(lèi)越“胖”,越難測(cè)試
不同職責(zé)對(duì)應(yīng)的依賴(lài)越來(lái)越多,編寫(xiě)單元測(cè)試也越來(lái)越麻煩。
(4)不利于復(fù)用
你可能想復(fù)用其中一部分能力,卻不得不引入整個(gè)龐大的類(lèi)。
SRP 的目標(biāo)是:讓變化各自為政,而不是綁在一起互相拖累。
二、SRP 的定義與本質(zhì)
Uncle Bob 的經(jīng)典定義是:如果一個(gè)類(lèi)有多個(gè)變化原因,那么它就承擔(dān)了多個(gè)職責(zé)。其關(guān)鍵不在于功能的數(shù)量,而在于變化來(lái)源的不同。
例如,下面這個(gè)“訂單處理器”類(lèi)承擔(dān)了三個(gè)彼此獨(dú)立的變化來(lái)源:
def notify(self, order): ... # 通知方式(因系統(tǒng)升級(jí)而變)這三類(lèi)變化互不相關(guān),擠在同一個(gè)類(lèi)里便違反了 SRP。
SRP 的本質(zhì)是:
? 職責(zé)決定變化
? 變化決定邊界
? 邊界決定代碼結(jié)構(gòu)
一個(gè)“職責(zé)”就是一種“未來(lái)可能變化的方向”。
現(xiàn)實(shí)類(lèi)比:為什么 SRP 如此重要?
想象學(xué)校里一位老師同時(shí)負(fù)責(zé):教語(yǔ)文、數(shù)學(xué)、歷史、體育、心理輔導(dǎo)、班級(jí)行政管理。如果學(xué)校對(duì)“體育課”進(jìn)行課程改革:
? 這位老師需要調(diào)整體育課教學(xué)方案
? 但語(yǔ)文課、數(shù)學(xué)課、行政任務(wù)等都可能被連帶影響(時(shí)間沖突、工作壓力增大等)
? 其他課程也可能被迫改變排課方式
這種變化互相干擾,就是 SRP 試圖避免的局面。
如果每個(gè)老師只負(fù)責(zé)一門(mén)學(xué)科,那么課程改革僅影響對(duì)應(yīng)老師,不會(huì)牽連全校。
SRP 做的就是:把變化來(lái)源分開(kāi),讓一個(gè)需求變化只影響一塊代碼,而不是“牽一發(fā)而動(dòng)全身”。
三、Python 中常見(jiàn)的 SRP 違例場(chǎng)景
Python 靈活的語(yǔ)法常導(dǎo)致類(lèi)很快變得臃腫。
以下是一些典型反例:
(1)“全能處理器 / God Class”
例如一個(gè)視圖類(lèi)同時(shí)處理:數(shù)據(jù)校驗(yàn)、ORM 查詢(xún)、業(yè)務(wù)邏輯、異常處理、序列化、日志、通知推送。一旦任何一部分發(fā)生變化,都必須修改同一個(gè)類(lèi)。
(2)模型類(lèi)承擔(dān)業(yè)務(wù)行為
例如 Django model 中塞入校驗(yàn)規(guī)則、折扣計(jì)算、推送通知、導(dǎo)出報(bào)表,導(dǎo)致模型類(lèi)因不同變化頻繁變動(dòng)。
(3)工具類(lèi)變成“垃圾桶”
當(dāng) utils.py 中含有幾十種不相關(guān)的方法時(shí),它本質(zhì)上已經(jīng)不是“一個(gè)職責(zé)”,而是“多個(gè)變化來(lái)源的集合”。
(4)控制器負(fù)責(zé)“所有事情”
例如:
...一次需求改動(dòng)可能讓整個(gè)函數(shù)全部變動(dòng)。
四、如何遵守 SRP:拆分的正確方式
(1)按變化來(lái)源拆分類(lèi)(最核心方法)
從“訂單處理器”示例重新設(shè)計(jì):
self.notifier.notify(order)職責(zé)明確、變化隔離。
(2)使用抽象類(lèi)(ABC)或協(xié)議(Protocol)隔離職責(zé)
為職責(zé)定義接口,讓不同實(shí)現(xiàn)各自獨(dú)立變化。
...(3)函數(shù)級(jí)別也適用 SRP
如果一個(gè)函數(shù)負(fù)責(zé)校驗(yàn)、計(jì)算、轉(zhuǎn)換格式,那么它承擔(dān)了三個(gè)變化來(lái)源。建議拆分為:
def format_response(order): ...(4)適度拆分,不要過(guò)度設(shè)計(jì)
如果兩個(gè)功能總是一起變化,那么它們屬于同一職責(zé),不用拆。
例如 Point 與 distance 計(jì)算、User 對(duì)象與其基本字段轉(zhuǎn)換,通常不必分開(kāi)。
SRP 的目的是降低變化的影響,而不是追求碎片化設(shè)計(jì)。
五、實(shí)例:用 SRP 優(yōu)化一個(gè)現(xiàn)實(shí)場(chǎng)景
不符合 SRP:一個(gè)類(lèi)承擔(dān)所有職責(zé)
def send_email(self, pdf): ...四種變化來(lái)源:數(shù)據(jù)來(lái)源變、分析規(guī)則變、渲染方式變、通知渠道變。任何一處變化都會(huì)影響整個(gè)類(lèi)。
符合 SRP:職責(zé)清晰的設(shè)計(jì)
self.sender.send(pdf)每個(gè)類(lèi)都只有一個(gè)變化原因,類(lèi)結(jié)構(gòu)更加可靠。
小結(jié)
單一職責(zé)原則要求一個(gè)類(lèi)應(yīng)當(dāng)僅有一個(gè)引起變化的原因。它并不限制一個(gè)類(lèi)做多少事,而是強(qiáng)調(diào)“一個(gè)類(lèi)的所有行為必須屬于同一種變化來(lái)源”。遵循 SRP 可帶來(lái)更穩(wěn)定的設(shè)計(jì)、更低的維護(hù)成本、更高的可擴(kuò)展性以及更易理解與測(cè)試的代碼結(jié)構(gòu)。在 Python 這類(lèi)動(dòng)態(tài)語(yǔ)言中,由于缺乏編譯期檢查,更需開(kāi)發(fā)者主動(dòng)運(yùn)用 SRP 來(lái)構(gòu)建健壯的代碼模型。作為 SOLID 原則的基石,SRP 幫助代碼在變化中保持穩(wěn)定,而非陷入混亂。
![]()
“點(diǎn)贊有美意,贊賞是鼓勵(lì)”
特別聲明:以上內(nèi)容(如有圖片或視頻亦包括在內(nèi))為自媒體平臺(tái)“網(wǎng)易號(hào)”用戶(hù)上傳并發(fā)布,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.