简介
前面我们已经介绍了
- SRP ,单一职责原则
- OCP,开闭原则
- LSP,里式替换原则
今天来讲解接口隔离原则。
接口是Java 编程语言的核心功能之一,在企业应用开发中被广泛应用来抽象业务和多接口的实现能力。从写代码的角度,写一个接口是简单的,你可以用 interface
关键字创建一个接口并在接口里声明一些方法,其他的类可以通过 implements
关键字来实现接口定义的方法。作为一个 java 使用者,你肯定写过很多接口,但是有没有想过这样一个问题:之前写的接口是否是合理的,有没有好的设计原则可以指导我们编写出良好的接口? 下面详解的 接口隔离原则
可以回答这个问题。
接口隔离原则(Interface Segregation Principle,简称ISP
)代表SOLID
原则中的I
,SOLID
原则是面向对象编程中设计原则,遵循这些原则可使我们的代码易读、易维护、易升级、易修改。接口隔离原则是 Robert C.Martin 在2002年出的一本书 《Agile Software Development: Principles, Patterns and Practices》 里提出的,原则的描述是 Clients should not be forced to depend upon interfaces that they do not use
,直译为: 客户端不应该被强迫依赖它不需要的接口。其中客户端是指实现接口的类。
接口隔离原则告诉我们接口不应包含实现类不需要的方法,否则,这类接口我们称之为胖接口
,实现胖接口的类要为那些不需要的方法提供空实现,除此之外,当接口发生变化时实现类也会被迫修改,尤其是那些使用不到的方法签名发生变化。接口隔离原则主张把胖接口分离成高内聚的小接口,这些小接口被称为角色接口
。每个角色接口只包含与他紧密相关的方法,从而,实现类只实现与它相关联的接口。
违反接口隔离原则的例子
有一个需求,开发一个生产不同类型的玩具的应用,每个玩具会有价格和颜色的基本属性,如汽车和火车有移动的行为,但另一些玩具,如飞机有移动和飞的行为。因此,我们定义一个接口
1
2
3
4
5
6
7public interface Toy { void setPrice(double price); void setColor(String color); void move(); void fly(); }
如果玩具是飞机,它能实现这个接口的所有方法,如过是房子,只能实现部分方法,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public class ToyHouse implements Toy { double price; String color; @Override public void setPrice(double price) { this.price = price; } @Override public void setColor(String color) { this.color=color; } @Override public void move(){} @Override public void fly(){} }
如代码所示,类ToyHouse
提供了方法 move
、fly
的空实现。此时接口Toy
的设计违反了ISP,ToyHouse
的实现会影响代码的可读性和是维护代码的工程师困惑:为啥这些方法只提供了空实现呢。
违反ISP也可能导致违反开闭原则,继续以上的例子来讲,如果为Toy
添加一个 walk()
方法,那么实现Toy
的类都将被修改,违反了对修改关闭的原则。
遵循接口隔离原则
为了满足ISP,你可以把胖接口Toy
分离成三个小接口: Toy
,Movable
,Flyable
, 把方法move
移到接口Movable
,方法fly()
移到接口Flyable
,让实现类按需实现相应的接口。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118public interface Toy { void setPrice(double price); void setColor(String color); } public interface Movable { void move(); } public interface Flyable { void fly(); } public class ToyHouse implements Toy { double price; String color; @Override public void setPrice(double price) { this.price = price; } @Override public void setColor(String color) { this.color=color; } @Override public String toString(){ return "ToyHouse: Toy house- Price: "+price+" Color: "+color; } } public class ToyCar implements Toy, Movable { double price; String color; @Override public void setPrice(double price) { this.price = price; } @Override public void setColor(String color) { this.color=color; } @Override public void move(){ System.out.println("ToyCar: Start moving car."); } @Override public String toString(){ return "ToyCar: Moveable Toy car- Price: "+price+" Color: "+color; } } public class ToyPlane implements Toy, Movable, Flyable { double price; String color; @Override public void setPrice(double price) { this.price = price; } @Override public void setColor(String color) { this.color=color; } @Override public void move(){ System.out.println("ToyPlane: Start moving plane."); } @Override public void fly(){ System.out.println("ToyPlane: Start flying plane."); } @Override public String toString(){ return "ToyPlane: Moveable and flyable toy plane- Price: "+price+" Color: "+color; } } public class ToyBuilder { public static ToyHouse buildToyHouse(){ ToyHouse toyHouse=new ToyHouse(); toyHouse.setPrice(15.00); toyHouse.setColor("green"); return toyHouse; } public static ToyCar buildToyCar(){ ToyCar toyCar=new ToyCar(); toyCar.setPrice(25.00); toyCar.setColor("red"); toyCar.move(); return toyCar; } public static ToyPlane buildToyPlane(){ ToyPlane toyPlane=new ToyPlane(); toyPlane.setPrice(125.00); toyPlane.setColor("white"); toyPlane.move(); toyPlane.fly(); return toyPlane; } } public class ToyBuilderTest { @Test public void testBuildToyHouse() throws Exception { ToyHouse toyHouse=ToyBuilder.buildToyHouse(); System.out.println(toyHouse); } @Test public void testBuildToyCar() throws Exception { ToyCar toyCar=ToyBuilder.buildToyCar();; System.out.println(toyCar); } @Test public void testBuildToyPlane() throws Exception { ToyPlane toyPlane=ToyBuilder.buildToyPlane(); System.out.println(toyPlane); } }
总结
接口隔离原则和单一职责原则有同样的目的:确保设计出小的、聚焦的、高内聚的软件组件。两者不同的是,单一职责原则关心的是类,接口隔离原则关心的是接口。接口隔离原则比较容易理解和使用。但是,也要当心防止接口被分割的过细,因此,需要思考接口的职责范围,可以根据角色划分接口。
除了我们上面讲解的java 编程语言的接口外,我们常讲的接口还有两类
- 一类是一组接口集合,常见是微服务的接口和类库的接口。
- 另一类是大个 API 接口或者函数,调用者只需要函数中的部分功能,我们需要把大而全的函数拆分成粒度更细的多个函数,让调用者只依赖它需要的那个细粒度的函数。
以上两类接口,从更广泛的角度理解,ISP 也同样适用,可以指导我们在高层次上进行设计。
最后
以上就是留胡子斑马最近收集整理的关于SOLID原则-接口隔离原则的全部内容,更多相关SOLID原则-接口隔离原则内容请搜索靠谱客的其他文章。
发表评论 取消回复