接口隔离原则 - Interface Segregation Principle
1.介绍:客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上
设计应用程序的时候,如果一个模块包含多个子模块,那么我们应该小心对模块做出抽象。设想该模块由一个类实现,我们可以把系统抽象成一个接口。但是要添加一个新的模块扩展程序时,如果要添加的模块只包含原系统中的一些子模块,那么系统就会强迫我们实现接口中的所有方法,并且清寒要编写一些哑方法。这样的接口被称为肚胖接口或者被污染的接口,使用这样的接口将会给系统引入一些不当的行为,这些不当的行为可能导致不正确的结果,也可能导入资源浪费。
2.类A通过接口Interface1依赖类B,类C通过接口Interface1依赖类D,如果接口有Interface1对于类A和类C来说不是最小接口,那么类B和类D必须去实现他们不需要的方法
3.接口隔离原则应当这样处理:
将接口Interface1拆分独立的几个接口,类A和类C分别与他们需要的接口建立依赖关系,也就是采用接口隔离原则
4.使用接口隔离原则改进
1)类A通过接口与Interface1依赖类B,类C通过接口Interface1依赖类D,如果Interface1对于类A与类C来说不是做小接口,那么类B和类D必须去实现他们不需要的方法
2)将接口Interface1拆分为独立的接口,类A和类C分别与他们需要大的接口建立依赖关系,也就是采用接口隔离原则
3)接口Interface1中出现的方法,根据实际情况将Interface1·1拆分为三个接口
4)UML类图
5.代码实现:
没有实现接口隔离原则的代码,类B与类D实现过多接口中的方法,造成了资源浪费
public class Segregation1 {
public static void main(String[] args) {
A a = new A();
C c = new C();
a.depend1(new B());
a.depend2(new B());
a.depend3(new B());
}
}
//接口定义
interface Interface1 {
void operation1();
void operation2();
void operation3();
void operation4();
void operation5();
}
class B implements Interface1 {
public void operation1() {
System.out.println("B 实现 operation1");
}
public void operation2() {
System.out.println("B 实现 operation2");
}
public void operation3() {
System.out.println("B 实现 operation3");
}
public void operation4() {
System.out.println("B 实现 operation4");
}
public void operation5() {
System.out.println("B 实现 operation5");
}
}
class D implements Interface1 {
public void operation1() {
System.out.println("D 实现 operation1");
}
public void operation2() {
System.out.println("D 实现 operation2");
}
public void operation3() {
System.out.println("D 实现 operation3");
}
public void operation4() {
System.out.println("D 实现 operation4");
}
public void operation5() {
System.out.println("D 实现 operation5");
}
}
class A {
public void depend1(Interface1 i) {
i.operation1();
}
public void depend2(Interface1 i) {
i.operation2();
}
public void depend3(Interface1 i) {
i.operation3();
}
}
class C {
public void depend1(Interface1 i) {
i.operation1();
}
public void depend4(Interface1 i) {
i.operation4();
}
public void depend5(Interface1 i) {
i.operation5();
}
}
实现接口隔离原则后的代码,达到了最小接口原理
public class Segregation1 {
public static void main(String[] args) {
A a = new A();
a.depend1(new B()); // A类通过接口去依赖B类
a.depend2(new B());
a.depend3(new B());
C c = new C();
c.depend1(new D()); // C类通过接口去依赖(使用)D类
c.depend4(new D());
c.depend5(new D());
}
}
// 接口1
interface Interface1 {
void operation1();
}
// 接口2
interface Interface2 {
void operation2();
void operation3();
}
// 接口3
interface Interface3 {
void operation4();
void operation5();
}
class B implements Interface1, Interface2 {
public void operation1() {
System.out.println("B 实现了 operation1");
}
public void operation2() {
System.out.println("B 实现了 operation2");
}
public void operation3() {
System.out.println("B 实现了 operation3");
}
}
class D implements Interface1, Interface3 {
public void operation1() {
System.out.println("D 实现了 operation1");
}
public void operation4() {
System.out.println("D 实现了 operation4");
}
public void operation5() {
System.out.println("D 实现了 operation5");
}
}
class A { // A 类通过接口Interface1,Interface2 依赖(使用) B类,但是只会用到1,2,3方法
public void depend1(Interface1 i) {
i.operation1();
}
public void depend2(Interface2 i) {
i.operation2();
}
public void depend3(Interface2 i) {
i.operation3();
}
}
class C { // C 类通过接口Interface1,Interface3 依赖(使用) D类,但是只会用到1,4,5方法
public void depend1(Interface1 i) {
i.operation1();
}
public void depend4(Interface3 i) {
i.operation4();
}
public void depend5(Interface3 i) {
i.operation5();
}
}
对接口的污染
过于臃肿的接口设计是对接口的污染。所谓的接口污染就是为接口添加不必要的职责,如果开发人员在接口中增加一个新功能的目的只是减少接口实现类的数目,则此设计将导致接口被不断地“污染”并“变胖”。
“接口隔离”其实就是定制化服务设计的原则。使用接口的多重继承实现对不同的接口的组合,从而对外提供组合功能---达到“按需提供服务”。 接口即要拆,但也不能拆得太细,这就得有个标准,这就是高内聚。接口应该具备一些基本的功能,能独一完成一个基本的任务。
在实际应用中,会遇到如下问题:比如,我需要一个能适配多种类型数据库的 DAO 实现,那么首先应实现一个数据库操作的接口,其中规定一些数据库操作的基本方法,比如连接数据库、增删改查、关闭数据库等。这是一个最少功能的接口。对于一些 MySQL 中特有的而其他数据库里并不存在的或性质不同的方法,如 PHP 里可能用到的 MySQL 的 pconnect 方法,其他数据库里并不存在和这个方法相同的概念,这个方法也就不应该出现在这个基本的接口里,那这个基本的接口应该有哪些基本的方法呢?PDO已经告诉你了。
PDO 是一个抽象的数据库接口层,它告诉我们一个基本的数据库操作接口应该实现哪些基本的方法。接口是一个高层次的抽象,所以接口里的方法都应该是通用的、基本的、不易变化的。
还有一个问题,那些特有的方法应该怎么实现?根据ISP原则,这些方法可以在别一个接口中存在,让这个“异类”同时实现这两个接口。
对于接口的污染,可以考虑这两条处理方式:
-
利用委托分离接口。
-
利用多继承分离接口。
委托模式中,有两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理,如策略模式、代理模式等中都应用到了委托的概念。