userserviceimpl 不该实现包含 deleteuserbyid 和 login 的大接口,因为违反接口隔离原则(isp):不同角色调用方需不同方法,混在一起导致权限失控、测试冗余、扩展困难;应按角色拆分为 userqueryservice、userauthservice、adminuserservice 等专用接口。

为什么 UserServiceImpl 不该实现一个包含 deleteUserById 和 login 的大接口
因为调用方(比如前端 Web 层)根本不需要、也不该看到管理员删用户的入口,硬塞在一起会导致:编译期无报错,但运行时权限失控;测试要 mock 一堆不用的方法;新增角色(如审计员)时又得改接口——牵一发而动全身。
ISP 的核心不是“多写几个 interface”,而是让每个接口只对一类使用者负责。比如:
-
UserQueryService:只含getUserInfoById、getUserInfoByCellphone -
UserAuthService:只含register、login -
AdminUserService:只含deleteUserById、deleteUserByCellphone
这样,Web 控制器只需依赖 UserQueryService 和 UserAuthService,连 AdminUserService 的类名都不会出现在它的 import 里。
Java 中拆接口时最容易踩的三个坑
很多人拆着拆着就变成“为拆而拆”,结果更难维护。关键判断标准只有一个:有没有不同角色的调用者?
立即学习“Java免费学习笔记(深入)”;
- 把
save和validate塞进同一个接口——但校验逻辑常被单元测试直接调,而save只走 service 层。应拆成Validator和Persister - 给所有 DAO 方法加
throws SQLException,但上层 service 其实只关心业务异常。应让 DAO 接口抛RuntimeException子类(如DataAccessException),避免上层被迫捕获无关 checked exception - 接口里放默认方法做通用逻辑(比如日志埋点),结果发现某些实现类根本不希望被埋点。默认方法≠可选,它仍是接口契约的一部分——真要可选,就该抽成独立
Loggable接口
implements A, B, C 是好信号,还是坏味道?
当一个类同时实现多个接口时,先看它们是否天然属于同一职责维度。比如:
- 好:
UserServiceImpl implements UserQueryService, UserAuthService—— 都是“用户”这个域内的行为 - 坏:
OrderServiceImpl implements PaymentService, EmailService, SmsService—— 这不是职责聚合,是职责污染。支付、发短信、发邮件应由各自领域对象完成,OrderService只需定义回调钩子(如onPaymentSuccess())
如果发现一个实现类总在补空方法(throw new UnsupportedOperationException()),说明接口没拆对,或者不该由它来实现这个接口。
和单一职责原则(SRP)的区别在哪?
SRP 管的是「一个类」的职责边界;ISP 管的是「一个接口」对谁暴露、暴露多少。它们经常一起生效,但冲突点很明确:
- 一个类可以只做一件事(符合 SRP),但它实现的大接口却暴露了 12 个方法,其中 8 个只有后台任务用——这就违反 ISP
- 反过来,你拆出 5 个极小接口(
CanRead、CanWrite、CanDelete…),但每个都只被一个类实现,且这 5 个类永远成组出现——这时 ISP 没问题,但可能暗示领域建模太碎,该回归统一抽象
真正难的不是拆接口,而是判断哪个调用方“算一个独立角色”。权限系统、灰度开关、模块化部署边界,才是决定接口粒度的真实依据。










