不该。这违反单一职责原则(srp),登录与订单查询是两个独立变化原因,混在一起导致耦合高、测试难;应拆分为 authservice 和 orderservice,工具类和 controller 同理需严格划清职责边界。

一个类该不该同时处理用户登录和订单查询
不该。这直接违反单一职责原则(SRP),因为登录逻辑变更(比如接入微信扫码)和订单查询逻辑变更(比如加缓存策略)是两个完全独立的“变化原因”。混在一起,改一处就可能牵连另一处,测试成本翻倍。
常见错误现象:UserManager 类里既有 login() 又有 getOrderList();调用方传个 userId 却要依赖整个用户模块才能查订单,耦合过重。
- 判断依据:如果描述这个类时要用“和”“或”连接功能(如“用户认证和权限校验”),大概率已违规
- 正确做法:拆成
AuthService(只管登录、登出、token 验证)和OrderService(只管查单、改单、退单) - 注意边界:不要为了拆而拆——
AuthServiceImpl里调用TokenGenerator.generate()是合理的,但若它还自己读写数据库日志表,就又多了一个变化原因
工具类为什么最容易违反 SRP
因为“工具”听起来很中性,容易把所有杂活塞进去。比如一个叫 Utils 的类,今天加个文件读取,明天加个 HTTP 请求封装,后天再塞个 JSON 格式化——它已经成了变化原因最多的类。
使用场景:前端项目里常有 common/utils.js,后端 Spring Boot 项目里常有 SystemUtils.java。
- 典型错误:
FileUtils.readFile()和FileUtils.sendEmail()出现在同一个类里——文件操作和邮件发送毫无关联 - 参数差异不重要,关键是职责归属:读写文件归
FileHandler,发邮件归EmailClient,哪怕它们都用String做参数 - 性能影响:大而全的工具类加载慢、IDE 索引卡顿、单元测试难写(你得 mock 整个网络层才能测一个文件方法)
Controller 层怎么才算守住了 SRP
Controller 的唯一职责是“接收请求、转发给 Service、包装响应”,它不该做任何业务判断、数据转换或日志记录。
常见错误现象:UserController 里出现 if (user.getAge() 或者直接调用 <code>logger.info() 记录用户行为。
- 业务校验应下沉到
UserService或独立的Validator类,Controller 只负责把参数传过去 - 日志记录统一交给 AOP 切面或拦截器,而不是每个接口手动写
log.info("req: {}") - 兼容性提醒:Spring 的
@Valid注解校验是 OK 的,因为它属于请求层面的约束,不掺杂业务规则
实体类(PO/DTO)什么时候算破戒了
当它开始“主动做事”,而不仅是“被动承载数据”时,就破戒了。比如 User 类里写了个 sendWelcomeEmail() 方法,或者 OrderDTO 自己去调用 PaymentService.pay()。
使用场景:MyBatis 的 User 实体类、前端 Vue 的 userForm 对象。
- 安全红线:实体类里绝不该出现数据库操作、HTTP 调用、线程调度等任何外部交互
- 容易被忽略的点:getter/setter 里加逻辑(如
getFullName()拼接 first + last)是允许的,但saveToDB()就绝对不行 - 边界模糊处:DTO 转 VO 的映射逻辑,应该放在
Converter类里,而不是让UserDTO自己实现toVO()
UserService 同时管注册和登录可能没问题;但一旦要支持 OAuth2、短信登录、游客模式,就必须拆。别等报错才拆,等日志里出现 “修改登录流程导致注册接口超时” 这种问题,就晚了。










