单一职责原则(Single Responsibility Principle,SRP)是 SOLID 五大原则中的第一条,由 Robert C. Martin(“Uncle Bob”)提出。它是软件设计中最基础、也最容易被误用的原则。和它的名字不同,它并不是“一个类只做一件事”那么简单,而是:
一个类应该只有一个引起其变化的原因。
A class should have only one reason to change.
SRP 专注于“变化来源”。它要求开发者将软件中不同变化的方向分离,使每个类(或模块)对一种变化负责。
如果一个类承担了多种不同类型的职责,那么任何一种职责发生变化时,都可能迫使类被修改,最终导致代码变得脆弱、庞大、难以维护。
一、为什么需要单一职责原则?
软件之所以会难维护,很大程度上是因为“多个变化纠缠在一起”。SRP 通过“解耦变化来源”来提高可维护性。
(1)多个职责会让类变得不稳定
例如,当日志、业务逻辑、数据存储、验证规则混在一个类里时,其中任何一个需求变更都会让整个类被修改。
(2)修改一个功能容易破坏另一个功能
当职责混合时,影响范围无法隔离。一个修改可能引发连锁错误。
(3)类越“胖”,越难测试
不同职责对应的依赖越来越多,编写单元测试也越来越麻烦。
(4)不利于复用
你可能想复用其中一部分能力,却不得不引入整个庞大的类。
SRP 的目标是:让变化各自为政,而不是绑在一起互相拖累。
二、SRP 的定义与本质
Uncle Bob 的经典定义是:如果一个类有多个变化原因,那么它就承担了多个职责。其关键不在于功能的数量,而在于变化来源的不同。
例如,下面这个“订单处理器”类承担了三个彼此独立的变化来源:
def notify(self, order): ... # 通知方式(因系统升级而变)这三类变化互不相关,挤在同一个类里便违反了 SRP。
SRP 的本质是:
• 职责决定变化
• 变化决定边界
• 边界决定代码结构
一个“职责”就是一种“未来可能变化的方向”。
现实类比:为什么 SRP 如此重要?
想象学校里一位老师同时负责:教语文、数学、历史、体育、心理辅导、班级行政管理。如果学校对“体育课”进行课程改革:
• 这位老师需要调整体育课教学方案
• 但语文课、数学课、行政任务等都可能被连带影响(时间冲突、工作压力增大等)
• 其他课程也可能被迫改变排课方式
这种变化互相干扰,就是 SRP 试图避免的局面。
如果每个老师只负责一门学科,那么课程改革仅影响对应老师,不会牵连全校。
SRP 做的就是:把变化来源分开,让一个需求变化只影响一块代码,而不是“牵一发而动全身”。
三、Python 中常见的 SRP 违例场景
Python 灵活的语法常导致类很快变得臃肿。
以下是一些典型反例:
(1)“全能处理器 / God Class”
例如一个视图类同时处理:数据校验、ORM 查询、业务逻辑、异常处理、序列化、日志、通知推送。一旦任何一部分发生变化,都必须修改同一个类。
(2)模型类承担业务行为
例如 Django model 中塞入校验规则、折扣计算、推送通知、导出报表,导致模型类因不同变化频繁变动。
(3)工具类变成“垃圾桶”
当 utils.py 中含有几十种不相关的方法时,它本质上已经不是“一个职责”,而是“多个变化来源的集合”。
(4)控制器负责“所有事情”
例如:
...一次需求改动可能让整个函数全部变动。
四、如何遵守 SRP:拆分的正确方式
(1)按变化来源拆分类(最核心方法)
从“订单处理器”示例重新设计:
self.notifier.notify(order)职责明确、变化隔离。
(2)使用抽象类(ABC)或协议(Protocol)隔离职责
为职责定义接口,让不同实现各自独立变化。
...(3)函数级别也适用 SRP
如果一个函数负责校验、计算、转换格式,那么它承担了三个变化来源。建议拆分为:
def format_response(order): ...(4)适度拆分,不要过度设计
如果两个功能总是一起变化,那么它们属于同一职责,不用拆。
例如 Point 与 distance 计算、User 对象与其基本字段转换,通常不必分开。
SRP 的目的是降低变化的影响,而不是追求碎片化设计。
五、实例:用 SRP 优化一个现实场景
不符合 SRP:一个类承担所有职责
def send_email(self, pdf): ...四种变化来源:数据来源变、分析规则变、渲染方式变、通知渠道变。任何一处变化都会影响整个类。
符合 SRP:职责清晰的设计
self.sender.send(pdf)每个类都只有一个变化原因,类结构更加可靠。
小结
单一职责原则要求一个类应当仅有一个引起变化的原因。它并不限制一个类做多少事,而是强调“一个类的所有行为必须属于同一种变化来源”。遵循 SRP 可带来更稳定的设计、更低的维护成本、更高的可扩展性以及更易理解与测试的代码结构。在 Python 这类动态语言中,由于缺乏编译期检查,更需开发者主动运用 SRP 来构建健壮的代码模型。作为 SOLID 原则的基石,SRP 帮助代码在变化中保持稳定,而非陷入混乱。
“点赞有美意,赞赏是鼓励”
热门跟贴