接口隔离原则(Interface Segregation Principle,ISP)要求程序员尽量将臃肿庞大的接口拆分成更小的和更具体的接口,让接口中只包含客户感兴趣的方法。

与之前介绍的单一职责原则类似,接口隔离原则同样是为了提高类的内聚性、降低它们之间的耦合性,都是面向对象三个基本特征中的“封装”思想的体现。

但两者也存在不同之处:

  单一职责 接口隔离
作用目标 接口
范围级别 实现细节 整体框架
侧重方面 职责划分 接口依赖

这里重点说一下“接口依赖”与“接口实现”的区别:

依赖(Dependency)关系是一种使用关系,它是对象之间耦合度最弱的一种关联方式,是临时性的关联。

在代码中,某个类的方法通过局部变量方法的参数或者对静态方法的调用来访问另一个类(被依赖类)中的某些方法来完成一些职责。

图形表示:依赖关系使用虚线箭头表示,使用类指向被依赖的类。(下图左侧的线)

实现(Realization)关系是接口与实现类之间的关系。在这种关系中,类实现了接口,类中的操作实现了接口中所声明的所有的抽象操作。

图形表示:实现关系使用带空心三角箭头的虚线来表示,箭头从实现类指向接口。(下图右侧的线)

面向对象设计模式原则05 接口隔离原则(ISP)_ide

如上面的UML类图所示:

下面以代码的方式说明两者的区别。

 1 package asen.yang;
 2 public interface IAction {
 3     public void eat();
 4 
 5     public void sleep();
 6 
 7     public void beatDD();
 8 }
 9 
10 public class CommonAction implements IAction {
11 
12     @Override
13     public void eat() {
14         System.out.println("吃饭");
15     }
16 
17     @Override
18     public void sleep() {
19         System.out.println("睡觉");
20     }
21 
22     @Override
23     public void beatDD() {
24         System.out.println("打豆豆");
25     }
26 }
27 
28 public class People {
29     private String Name;
30 
31     public People(String Name) {
32         this.Name = Name;
33     }
34 
35     protected void Action() {
36         IAction action = new CommonAction();
37         System.out.println("我是" + Name);
38         System.out.println("我的爱好有:");
39         action.eat();
40         action.sleep();
41         action.beatDD();
42     }
43 }
44 
45 public class ISP_Demo {
46     public static void main(String[] args) {
47         People p = new People("小明");
48         p.Action();
49     }
50 }

CommonAction类"实现"了IAction接口,因此要实现接口中所声明的所有方法。

而People类“依赖”IAction接口,在People类的Action()方法中,使用了IAction类型的变量action。

本原则所侧重的就是第二种形式的接口依赖关系。

 

假如除了人这个类别之外,还有一个狗类,而狗并不会打豆豆,只会吃饭睡觉。依照本原则,就应该将IAction拆分。

UML类图如下:

面向对象设计模式原则05 接口隔离原则(ISP)_封装_02

对应的代码可以重构为:

 1 package asen.yang;
 2 public interface ILowAction {
 3     public void eat();
 4     public void sleep();
 5 }
 6 
 7 public interface IHighAction{
 8     public void beatDD();
 9 }
10 
11 public class LowAction implements ILowAction {
12     @Override
13     public void eat() {
14         System.out.println("吃饭");
15     }
16 
17     @Override
18     public void sleep() {
19         System.out.println("睡觉");
20     }
21 }
22 
23 public class HighAction implements IHighAction{
24 
25     @Override
26     public void beatDD() {
27         System.out.println("打豆豆");
28     }
29 }
30 
31 public class People {
32     private String Name;
33 
34     public People(String Name) {
35         this.Name = Name;
36     }
37 
38     public void Action() {
39         ILowAction lowaction = new LowAction();
40         System.out.println("我是" + Name);
41         System.out.println("我的低级爱好有:");
42         lowaction.eat();
43         lowaction.sleep();
44         
45         IHighAction highaction = new HighAction();
46         System.out.println("我的高级爱好有:");
47         highaction.beatDD();
48     }
49 }
50 
51 public class Dog {
52     private String Name;
53 
54     public Dog(String Name) {
55         this.Name = Name;
56     }
57     
58     public void Action() {
59         ILowAction lowaction = new LowAction();
60         System.out.println("我是" + Name);
61         System.out.println("我的爱好有:");
62         lowaction.eat();
63         lowaction.sleep();
64     }
65 }
66 
67 public class ISP_Demo {
68     public static void main(String[] args) {
69         People p = new People("小明");
70         p.Action();
71         
72         Dog d = new Dog("汪汪");
73         d.Action();
74     }
75 }

将"吃饭"、"睡觉"这两种行为,是人与狗都有的共性的行为,单独封装在一个接口中。而将只有人具有的"打豆豆"这个行为,封装在单独的接口中。

这就是接口隔离原则的最直接体现。