来源:mikechen的互联网架构
接口隔离原则是实现高内聚、低耦合系统的基石。
接口隔离原则旨在解决接口设计的问题。通过将大接口拆分成多个小接口,让每个接口专注服务于一个子模块或业务逻辑,让系统更加灵活、可维护。
例如:家中的智能家居系统,有电视、电脑、窗帘、电灯等。
图左:遵循接口隔离
对具有相同功能的设备,配备独立遥控器,每个遥控器专注提供特定设备的控制按钮。
如果需要开灯,就用灯光遥控器,非常便捷。更改某个设备,也不会影响其他设备。
图右:不遵循接口隔离
通过一个多功能的遥控器,来控制所有设备。
遥控器按钮太多,很难快速找到需要的功能。
按钮之间耦合度高。修改任一设备,都可能牵一发而动全身。
要同时处理多种设备的功能,职责混杂,违背单一职责原则。
本文,我们深入接口隔离原则,力求通过图解、源码剖析到位。
大家好,我是 mikechen。
接口隔离是良好架构设计的基石,面试常考,非常重要。 为方便大家系统学习,我已将本文归纳到《阿里架构师进阶专题合集》。 需要的同学,拉到文末获取。
接口隔离原则( ISP),英文全称 Interface Segregation Principle 。
接口隔离原则是指不应该强行要求客户端依赖于它们不用的接口,类之间的依赖应该建立在最小的接口上面。
简单理解:
客户端需要什么接口,就提供什么接口,不需要的就不依赖。
将大接口拆分成多个小接口,每个接口只专注服务于一个子模块或业务逻辑。
使用抽象类或默认方法,来减少接口的改动对原有代码的影响。
接口隔离原则的提出,是为了解决接口过大所带来的问题,提高系统的灵活性和可维护性。
如果客户端依赖了不需要的接口,将增加客户端和接口之间的耦合度,与“高内聚、低耦合”的思想相矛盾。
如果一个接口中包含了很多不相关的方法,要实现这个接口的类,就必须实现所有的方法。
在软件设计最初,我们的想法是将相同功能的方法放在同一个接口里面。
理论上来说,这貌似没错,我们来看看如何设计。
例如:
类 A 通过接口 I 依赖类 B,类 C 通过接口 I 依赖类 D。
那么,对于类 A 和类 B 来说,接口 I 不是最小接口,则类 B 和类 D 必须去实现它们不需要的方法。
如图:
类 A 依赖接口 I 中的方法 1、方法 2、方法 3,类 B 是对类 A 依赖的实现。
类 C 依赖接口 I 中的方法 1、方法 4、方法 5,类 D 是对类 C 依赖的实现。
对于类 B 和类 D 来说,虽然都有用不到的方法(红色字体),但由于实现了接口 I,所以也必须要实现这些用不到的方法。
代码示例:
interface I {
public void method1();
public void method2();
public void method3();
public void method4();
public void method5();
}
class A{
public void depend1(I i){
i.method1();
}
public void depend2(I i){
i.method2();
}
public void depend3(I i){
i.method3();
}
}
class B implements I{
public void method1() {
System.out.println("类B实现接口I的方法1");
}
public void method2() {
System.out.println("类B实现接口I的方法2");
}
public void method3() {
System.out.println("类B实现接口I的方法3");
}
对于类 D 来说,method2 和 method3 不是必需的。
但是,由于接口 A 中有这两个方法,即使它们没有作用,在实现时,也要同时实现这两个方法。
public void method2() {}
public void method3() {}
public void method4() {
System.out.println("类D实现接口I的方法4");
}
public void method5() {
System.out.println("类D实现接口I的方法5");
}
}
public class Client{
public static void main(String[] args){
A a = new A();
a.depend1(new B());
a.depend2(new B());
a.depend3(new B());
C c = new C();
c.depend1(new D());
c.depend2(new D());
c.depend3(new D());
}
}
当接口过多时,对依赖于它的类来说,接口中的方法有没有用,实现类中都必须去实现这些方法。
这样的架构设计很不合理,明显与“高内聚、低耦合”的思想相悖。
在程序设计中,依赖几个专用的接口,要比依赖一个综合的接口更灵活。
为了遵循接口隔离原则,我们将一个庞大的接口,拆分为 3 个专用的接口。
例如,将接口 I 拆分为接口 I1、接口 I2、接口 I3。
代码示例:
interface I1 {
public void method1();
}
interface I2 {
public void method2();
public void method3();
}
interface I3 {
public void method4();
public void method5();
}
class A{
public void depend1(I1 i){
i.method1();
}
public void depend2(I2 i){
i.method2();
}
public void depend3(I2 i){
i.method3();
}
}
class B implements I1, I2{
public void method1() {
System.out.println("类B实现接口I1的方法1");
}
public void method2() {
System.out.println("类B实现接口I2的方法2");
}
public void method3() {
System.out.println("类B实现接口I2的方法3");
}
}
class C{
public void depend1(I1 i){
i.method1();
}
public void depend2(I3 i){
i.method4();
}
public void depend3(I3 i){
i.method5();
}
}
class D implements I1, I3{
public void method1() {
System.out.println("类D实现接口I1的方法1");
}
public void method4() {
System.out.println("类D实现接口I3的方法4");
}
public void method5() {
System.out.println("类D实现接口I3的方法5");
}
}
每个类只需实现与其功能相关的接口,不需要的方法则不用去实现,极大提高了系统的可维护性和灵活性。
需要注意的是,对接口拆分要合理:
1)接口拆分应该尽量小,且专注于一个明确定义的功能领域。
2)接口设计避免过大或过小。过大可能导致实现类需要实现大量不必要的方法;而过小可能导致需要实现的接口数量过多,系统设计会变得很复杂。
看到这里,是不是觉得接口隔离和单一职责有些相似?
其实不然,下面介绍。
从功能上来看,接口隔离和单一职责确实有一定的相似性。
两者都是为了降低它们之间的耦合性,体现了封装的思想。
但是,两者的侧重点却不同:
单一职责原则:关注类的职责,针对程序实现和细节,偏向于业务;
接口隔离原则:关注对接口依赖的隔离,针对抽象和程序整体框架的构建,偏向于架构设计。
单一职责原则的详解篇:吃透单一职责原则,100倍效果提升代码质量
两篇一定要结合看,更容易融会贯通。
接口隔离原则通过将大接口拆分成多个小接口,让每个接口只专注服务于一个子模块或者业务逻辑,让系统更加灵活、可维护。
接口隔离原则的核心是接口细化,接口细化要合理,结合系统的实际需求,以适度设计为前提,既小而精确。
建议收藏备用,划走就再也找不到了。
接口隔离原则是良好架构设计的基石,社/校招面试也常问,非常重要。