接口隔离

接口隔离原则

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

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

我们通过一个例子来说明。我们知道在Java中一个具体类实现了一个接口,那必然就要实现接口中的所有方法。如果我们有一个类A和类B通过接口I来依赖,类B是对类A依赖的实现,这个接口I5个方法。但是类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();
上一页
下一页