文章目录
-
-
- 单一职责原则 (SRP)
-
- [1. 定义](#1. 定义)
- [2. 分析与理解](#2. 分析与理解)
- [3. 实例:登录功能重构](#3. 实例:登录功能重构)
-
单一职责原则 (SRP)
1. 定义
单一职责原则是所有原则中最简单、也最基础的一个。它提供了两种等价的定义:
-
定义一 (从职责角度) : 一个对象应该只包含单一的职责,并且该职责被完整地封装在一个类中。 这个定义主要用于指导我们如何控制类的粒度大小。
- 英文定义: Every object should have a single responsibility, and that responsibility should be entirely encapsulated by the class.
-
定义二 (从变化角度) : 就一个类而言,应该仅有一个引起它变化的原因。 这个定义更具实践指导意义,因为它提供了一个判断标准:如果你能想到多于一个的动机去修改一个类,那么这个类就违反了单一职责原则。
- 英文定义: There should never be more than one reason for a class to change.
2. 分析与理解
-
职责与复用性的关系:
- 一个类(或者大到模块,小到方法)承担的职责越多,其内部的逻辑就越复杂,各个职责之间就可能存在耦合。
- 这将导致它的可复用性越小。因为当你只是想复用其中一个职责时,不得不把其他不相关的职责也一起引入,造成不必要的依赖和复杂性。
-
高内聚与低耦合的基石:
- 单一职责原则是实现软件设计高内聚、低耦合 的指导方针。
- 高内聚: 指的是一个类或模块的内部,所有元素(方法、属性)都是为了同一个目标(单一职责)而紧密协作的。遵循SRP可以让类的内聚性变得非常高。
- 低耦合: 指的是类与类之间的依赖关系要尽可能弱。如果一个类承担了多个职责,比如职责A和职责B,那么当因为职责A的需求变化而修改这个类时,可能会无意中影响到职责B的正常运作,这就是高耦合带来的风险。将职责分离,可以有效降低这种耦合。
- 单一职责原则是实现软件设计高内聚、低耦合 的指导方针。
-
最简单也最难的原则:
- 简单在于其概念非常容易理解。
- 难在于"职责"的划分并没有一个绝对的标准。一个"职责"的粒度是粗是细,在不同的业务场景和设计需求下有不同的界定。这需要设计人员具备较强的分析设计能力和相关的重构经验,才能准确地发现类的多重职责并将其分离。如果分离得过细,可能会导致类的数量剧增,反而增加了系统的复杂性。
3. 实例:登录功能重构
- a. 初始设计 (违反SRP)
- 场景 : 在一个基于Java的C/S系统中,"登录功能"最初由一个
Login类来实现。
- 场景 : 在一个基于Java的C/S系统中,"登录功能"最初由一个

-
职责分析 : 这个
Login类承担了过多的职责,我们可以识别出至少三个或更多的"变化原因":- UI界面与交互职责 :
init()(初始化界面组件),display()(显示窗口),validate()(验证用户输入)。如果需要更改界面布局、美化UI或者修改校验逻辑,都需要修改这个类。 - 数据持久化职责 :
findUser()(根据用户名和密码查询用户)。如果底层用户数据存储方式改变(例如从数据库换成文件,或者数据库表结构变化),需要修改这个类。 - 数据库连接职责 :
getConnection()(获取数据库连接)。如果数据库的地址、用户名密码、或连接池策略发生变化,需要修改这个类。 - 程序启动职责 :
main()(作为程序的入口)。如果程序启动逻辑需要调整,也需要修改这个类。
- UI界面与交互职责 :
-
b. 重构后的设计 (遵循SRP)
- 重构目标 : 将
Login类中不同的职责分离到不同的类中,让每个类只有一个"变化的原因"。
- 重构目标 : 将

图片描述 :重构后的UML类图。
MainClass依赖LoginForm。LoginForm依赖UserDAO。UserDAO依赖DBUtil。这四个类各司其职。
-
职责分离结果:
LoginForm(登录表单类)- 职责: 专门负责UI界面的显示和用户交互。
- 包含方法 :
init(),display(),validate()。 - 变化原因: 仅当UI界面或前端校验逻辑需要改变时,才修改此类。
UserDAO(用户数据访问对象类)- 职责: 专门负责与用户数据相关的持久化操作。
- 包含方法 :
findUser()。 - 变化原因: 仅当用户数据的存储和访问逻辑(如SQL语句)需要改变时,才修改此类。
DBUtil(数据库工具类)- 职责: 专门负责提供数据库连接。
- 包含方法 :
getConnection()。 - 变化原因: 仅当数据库的连接信息或方式需要改变时,才修改此类。
MainClass(主类)- 职责: 作为程序的入口,负责应用的启动和组装。
- 包含方法 :
main()。 - 变化原因: 仅当程序的启动流程需要改变时,才修改此类。
-
重构总结 : 通过这次重构,将一个臃肿的类拆分成了四个高内聚的类。每个类都只有一个明确的职责,结构更清晰,代码更易于理解、维护和复用。