【设计模式入门】用单一职责原则重构登录功能,代码清爽了!
文章目录
一、前言:为什么你的代码越来越难维护?
不知道你有没有遇到过这种情况:一个类写了几百上千行,里面塞了各种不相关的方法,改个小功能生怕影响其他地方,单元测试写起来也特别费劲。
这大概率就是单一职责原则(Single Responsibility Principle, SRP) 没遵守好的锅。它是 SOLID 五大设计原则的 “老大哥”,核心定义很简单:一个类应该只有一个引起它变化的原因,也就是只承担一个职责。
今天我们就以 Java 登录功能为例,手把手教你用单一职责原则重构代码,看看它到底有多香!
二、问题分析:原来的 Login 类到底有多 “臃肿”?
先看一下我们原始的Login类,它包含了这些方法:
- init():初始化操作
- display():界面展示逻辑
- validate():参数校验
- getConnection():获取数据库连接
- findUser():查询用户信息
- main():程序入口
问题出在哪?
这个类简直是个 “全能选手”,同时承担了 4 种完全不同的职责:
1.界面交互:display()负责展示登录界面
2.业务校验:validate()负责参数合法性校验
3.数据访问:getConnection()和findUser()负责数据库操作
4.程序入口:main()作为启动入口
这就导致了严重的耦合问题:
- 需求变了要改界面?可能不小心就影响到数据库逻辑
- 换个数据库连接方式?界面相关的代码也要跟着改
- 想复用数据库查询逻辑?只能把整个 Login 类搬过去,带一堆没用的界面方法
这完全违背了单一职责原则,是典型的 “上帝类” 设计。
某基于Java的C/S系统的“登录功能”通过如上登录类(Login)实现。
三、重构方案:按职责拆分,各司其职

根据单一职责原则,我们把原来的Login类拆分成 4 个独立的类,每个类只做一件事:
1.UI交互类:LoginView
只负责界面展示和用户输入获取,不处理任何业务逻辑。
public class LoginView {
// 展示登录界面
public void display() {
System.out.println("===== 用户登录 =====");
System.out.print("请输入用户名:");
}
// 获取用户输入
public String getUsername() {
Scanner scanner = new Scanner(System.in);
return scanner.nextLine();
}
public String getPassword() {
System.out.print("请输入密码:");
Scanner scanner = new Scanner(System.in);
return scanner.nextLine();
}
// 展示登录结果
public void showResult(boolean isSuccess) {
if (isSuccess) {
System.out.println("登录成功!");
} else {
System.out.println("用户名或密码错误!");
}
}
}
2.业务逻辑校验类:LoginValidator
只负责参数合法性校验,比如用户名 / 密码是否为空、长度是否符合要求。
public class LoginValidator {
// 校验用户名和密码是否合法
public boolean validate(String username, String password) {
if (username == null || username.trim().isEmpty()) {
System.out.println("用户名不能为空!");
return false;
}
if (password == null || password.length() < 6) {
System.out.println("密码长度不能少于6位!");
return false;
}
return true;
}
}
3.数据访问类:UserDAO
只负责和数据库交互,获取连接、查询用户信息,不关心界面和业务逻辑。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public class UserDAO {
// 获取数据库连接
public Connection getConnection() throws Exception {
Class.forName("com.mysql.cj.jdbc.Driver");
return DriverManager.getConnection(
"jdbc:mysql://localhost:3306/test", "root", "password");
}
// 根据用户名和密码查询用户
public boolean findUser(String username, String password) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = getConnection();
String sql = "SELECT * FROM user WHERE username=? AND password=?";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, username);
pstmt.setString(2, password);
rs = pstmt.executeQuery();
return rs.next();
} catch (Exception e) {
e.printStackTrace();
return false;
} finally {
// 关闭资源(简化写法)
try { if (rs != null) rs.close(); } catch (Exception e) {}
try { if (pstmt != null) pstmt.close(); } catch (Exception e) {}
try { if (conn != null) conn.close(); } catch (Exception e) {}
}
}
}
4.业务控制类:LoginService
负责串联各个模块,处理登录的核心流程,但不直接处理界面和数据库操作。
public class LoginService {
private LoginValidator validator;
private UserDAO userDAO;
public LoginService() {
this.validator = new LoginValidator();
this.userDAO = new UserDAO();
}
// 登录核心流程
public boolean login(String username, String password) {
// 1. 参数校验
if (!validator.validate(username, password)) {
return false;
}
// 2. 查询用户
return userDAO.findUser(username, password);
}
}
5.程序入口类:MainClass
只负责启动程序,调用业务逻辑,不包含其他功能。
public class MainClass {
public static void main(String[] args) {
LoginView view = new LoginView();
LoginService service = new LoginService();
// 1. 展示界面并获取输入
view.display();
String username = view.getUsername();
String password = view.getPassword();
// 2. 调用登录服务
boolean isSuccess = service.login(username, password);
// 3. 展示结果
view.showResult(isSuccess);
}
}
四、写在最后:单一职责原则的核心思想
很多人会误解单一职责原则,觉得 “拆分得越细越好”,其实不是这样的。它的核心是把变化的原因隔离开,让一个类只有一个变化的理由。
比如上面的例子:
- 界面变了,只需要改LoginView
- 校验规则变了,只需要改LoginValidator
- 数据库换了,只需要改UserDAO
- 登录流程调整,只需要改LoginService
这样的代码,不仅维护起来轻松,团队协作时也不容易冲突,后续扩展功能也更方便。
希望这个简单的登录功能重构案例,能帮你真正理解单一职责原则。下次写代码时,不妨停下来想一想:这个类是不是承担了太多职责?能不能拆分成更独立的模块?
小彩蛋:常见误区提醒
1.不要过度拆分:不是每个方法都要单独建类,比如只有一两行的工具方法,没必要为了拆分而拆分,反而会增加复杂度。
2.关注 “变化的原因”:判断是否需要拆分,关键看这个类的修改频率和修改原因,如果不同的需求会导致类被频繁修改,就该考虑拆分了。
3.SRP 也适用于方法和模块:不仅类要遵守单一职责,方法、模块也应该只做一件事,比如一个方法不要既查数据库又处理业务还发邮件。
更多推荐



所有评论(0)