接口隔离原则是什么
接口隔离原则(ISP)要求客户不应被迫依赖它们不使用的接口:把大而全的接口按调用方视角拆成若干角色接口,实现类不必用空方法或抛异常来占位,调用方的依赖面也随之收窄。它关注的是调用方的依赖视图,而不是实现内部的职责划分。
基本信息
| 问题形态 | 胖接口加上空实现或抛异常的占位方法 |
|---|---|
| 做法 | 按使用方视角拆分角色接口 |
| 收益 | 依赖更少,实现类负担更轻 |
| 与 SRP 的差别 | SRP 管修改原因,ISP 管调用方的依赖视图 |
典型使用场景
- 一个接口被多个调用方共用:各方其实只用到其中一部分方法
- 实现类被迫写空方法:说明接口对这类实现并不适用
- 测试替身实现困难:接口太大,桩件要覆盖大量用不到的方法
- 公共 API 演进:拆成角色接口之后各自独立变化
- 依赖装配复杂:细接口让装配关系一眼看清
怎么找到角色边界
- 从每个调用方出发,列出它实际用到的方法
- 把交集之外的方法分出去,形成新的接口
- 让实现类按需实现多个接口
- 公共的基础方法可以放进更小的共享接口
角色接口示例
角色接口示例
// 之前:Worker 声明 work 与 eat,机器人被迫实现空的 eat
// 之后:按调用方视角拆开
interface Workable { void work(); }
interface Feedable { void eat(); }
class Robot implements Workable { public void work() { ... } }拆分前后的差别
| 维度 | 胖接口 | 角色接口 |
|---|---|---|
| 实现负担 | 被迫实现用不到的方法 | 只实现需要的方法 |
| 依赖面 | 调用方依赖全部方法 | 只依赖用到的那部分 |
| 测试替身 | 需要覆盖大量空方法 | 只需实现少量方法 |
与 SRP 的差别值得记住:SRP 关心实现因为什么原因需要修改,ISP 关心调用方因为什么依赖而被牵连。
判断依赖是否过宽的方法很简单:看调用方实际调用了接口里的几个方法,用不到的部分就是多余的依赖。
拆分的前提是实现方确实存在差异;如果所有实现都用得上全部方法,那么现在的接口就是合适的。
公共 API 频繁拆接口:版本兼容负担会随拆分次数不断增长,需要先定发布策略。