接口隔离

接口隔离原则

The dependency of one class to another one should depend on the smallest possible interface.

接口隔离原则说的是客户端不应该被迫依赖于它不使用的方法。简单来说就是更小和更具体的瘦接口比庞大臃肿的胖接口好。不要对外暴露没有实际意义的接口。换一种说法就是类间的依赖关系应该建立在最小的接口上。这样说好像更难懂。胖接口的职责过多,很容易违反单一职责原则,也会导致实现类不得不抛出 UnsupportedOperationException 这样的异常,违反里氏替换原则。因此,应该将接口设计得更瘦。

我们通过一个例子来说明。我们知道在 Java 中一个具体类实现了一个接口,那必然就要实现接口中的所有方法。如果我们有一个类 A 和类 B 通过接口 I 来依赖,类 B 是对类 A 依赖的实现,这个接口 I 有 5 个方法。但是类 A 与类 B 只通过方法 1,2,3 依赖,然后类 C 与类 D 通过接口 I 来依赖,类 D 是对类 C 依赖的实现但是他们却是通过方法 1,4,5 依赖。那么是必在实现接口的时候,类 B 就要有实现他不需要的方法 4 和方法 5 而类 D 就要实现他不需要的方法 2 和方法 3,这简直就是一个灾难的设计。所以我们需要对接口进行拆分,就是把接口分成满足依赖关系的最小接口,类 B 与类 D 不需要去实现与他们无关接口方法。比如在这个例子中,我们可以把接口拆成 3 个,第一个是仅仅由方法 1 的接口,第二个接口是包含 2,3 方法的,第三个接口是包含 4,5 方法的。这样,我们的设计就满足了接口隔离原则。

接口之所以存在,是为了解耦。开发者常常有一个错误的认知,以为是实现类需要接口。其实是消费者需要接口,实现类只是提供服务,因此应该由消费者(客户端)来定义接口。理解了这一点,才能正确地站在消费者的角度定义 Role interface,而不是从实现类中提取 Header Interface。

案例:砖头

砖头(Brick)可以被建筑工人用来盖房子,也可以被用来正当防卫:

public class Brick {
  private int length;
  private int width;
  private int height;
  private int weight;

  public void build() {
    //...包工队盖房
  }

  public void defense() {
    //...正当防卫
  }
}

如果直接提取以下接口,这就是 Header Interface:

public interface BrickInterface {
  void buildHouse();
  void defense();
}

普通大众需要的是可以防卫的武器,并不需要用砖盖房子。当普通大众(Person)被迫依赖了自己不需要的接口方法时,就违反接口隔离原则。正确的做法是站在消费者的角度,抽象出 Role interface:

public interface BuildHouse {
    void build();
}

public interface StrickCompetence {
    void defense();
}

public class Brick implement BuildHouse, StrickCompetence {
}

有了 Role interface,作为消费者的普通大众和建筑工人就可以分别消费自己的接口:

// Worker.java
brick.build();

// Person.java
brick.strike();
上一页
下一页