这是我参与「掘金日新计划 · 2 月更文挑战」的第 8 天,点击查看活动详情
系列文章|源码
定义-是什么
接口隔离原则(Interface Segregation Principle, ISP)的含义:
Clients should not be forced to depend upon interfaces that they don't use.
- 客户端不应该依赖它不用的接口;它需要什么接口就提供什么接口,把不需要的接口剔除掉。 => 低耦合
-
- 如果客户依赖了不需要的接口,就要面临不需要的接口变动带来的风险
- 提倡客户不应被迫使用对其无用的方法或功能
The dependency of one class to another one should depend on the smallest possible interface.
- 类间的依赖关系应建立在最小的接口上。 => 高内聚
-
- 把没有关系的接口合并在一起,会形成一个臃肿的大接口,这是对职责分配和接口的污染
- 最小接口:满足项目需求的相似功能
- 把庞大臃肿的接口拆分成更小和更具体的接口
接口隔离:多个专门的接口 > 单一的总接口
思考-为什么
接口隔离原则符合我们常说的高内聚、低耦合的设计思想,可以使类具有很好的可读性、可扩展性和可维护性。我们在设计接口的时候,要多花时间去思考,要考虑业务模型,包括对以后有可能发生变更的地方还要做一些预判。所以,对于抽象、对于业务模型的理解是非常重要的。
接口隔离原则和单一职责原则
从功能上来看,接口隔离原则和单一职责原则都是为了提高类的内聚, 降低类之间的耦合, 体现了封装的思想。但二者还是有区别的。
(1)从原则约束来看: 接口隔离原则更关注的是接口依赖程度的隔离;而单一职责原则更加注重的是接口职责的划分。
(2)从接口的细化程度来看: 单一职责原则对接口的划分更加精细,而接口隔离原则注重的是相同功能的接口的隔离。接口隔离里面的最小接口有时可以是多个单一职责的公共接口。
(3)单一职责原则更加偏向对业务的约束;接口隔离原则更加偏向设计架构的约束。这个应该好理解,职责是根据业务功能来划分的,所以单一原则更加偏向业务;而接口隔离更多是为了“高内聚”,偏向架构的设计。
接口隔离原则的优点
接口隔离原则是为了约束接口、降低类对接口的依赖性,遵循接口隔离原则有以下 5 个优点。
- 将臃肿庞大的接口分解为多个粒度小的接口,可以预防外来变更的扩散,提高系统的灵活性和可维护性。
- 接口隔离提高了系统的内聚性,减少了对外交互,降低了系统的耦合性。
- 如果接口的粒度大小定义合理,能够保证系统的稳定性;然而,如果定义过小,则会造成接口数量过多,使设计复杂化;如果定义太大,灵活性降低,无法提供定制服务,给整体项目带来无法预料的风险。
- 使用多个专门的接口能够体现对象的层次,因为可以通过接口的继承,实现对总接口的定义。
- 能减少项目工程中的代码冗余。过大的大接口里面通常放置许多不用的方法,当实现这个接口的时候,被迫设计冗余的代码。
接口隔离原则的实现方法
在具体应用接口隔离原则时,应该根据以下几个规则来衡量。
- 接口要尽量小 不能出现Fat Interface;但是要有限度,首先不能违反单一职责原则(不能一个接口对应半个职责)。
- 接口要高内聚 在接口中尽量少公布public方法。 接口是对外的承诺,承诺越少对系统的开发越有利。
- 定制服务 只提供访问者需要的方法。例如,为管理员提供IComplexSearcher接口,为公网提供ISimpleSearcher接口。
- 接口的设计是有限度的 了解环境,拒绝盲从。每个项目或产品都有选定的环境因素,环境不同,接口拆分的标准就不同, 需要深入了解业务逻辑。
接口隔离原则的建议
- 一个接口只服务于一个子模块或业务逻辑;
- 通过业务逻辑压缩接口中的public方法;
- 已被污染了的接口,尽量去修改;若变更的风险较大,则采用适配器模式转化处理;
- 拒绝盲从;
应用-怎么用
案例:动物行为
// 动物行为
public interface IAnimalAction {
void eat(); // 吃
void fly(); // 飞
void swim(); // 游泳
}
// 狗
public class Dog implements IAnimalAction {
@Override
public void eat() {
}
@Override
public void fly() {
// 狗不会飞
}
@Override
public void swim() {
}
}
对于狗来说,其他两种行为都ok,但是不会飞。假如再增加一种鸟类动物大雁,大雁不会游泳。
那么使用接口隔离原则来实现的一种可能:将接口拆分
public interface IEatAnimalAction {
void eat();
}
public interface IFlyAnimalAction {
void fly();
}
public interface ISwimAnimalAction {
void swim();
}
public class Bird implements IFlyAnimalAction, IEatAnimalAction {
@Override
public void eat() {
}
@Override
public void fly() {
}
}
设计粒度的注意事项
-
设计粒度上太小会适得其反
-
设计粒度参考,一个接口只服务于一个子模块或者业务逻辑
接口隔离原则 vs 单一职责原则
-
约束偏向:接口隔离偏向约束架构设计,单一职责偏向约束业务
-
细化程度:单一职责更精细,接口隔离注重相似接口的隔离