实现接口隔离原则主要需要实现两个要求:

  • 客户端不应该依赖不需要的接口
  • 类间的依赖应该建立在最小的接口上

接口不应该是臃肿的,接口应该细化。这和单一职责有一定区别,单一职责要求类或者接口具有单一的职责,而接口隔离要求接口的实现的方法尽可能的少。
例如提供了PersonInfoService 如下所示. 对于PersonInfoService 来说,有的业务仅仅只需要判断Person的性别即可,并不需要完整的PersonInfoService ,这时候仅仅需要提供PersonSexService即可,这样就做到了简单的服务拆分。

  1. public interface PersonInfoService {
  2. String getPersonName(int personId);
  3. int getPersonAge(int personId);
  4. boolean isMan(int personId);
  5. }
  6. // 可以拆分为
  7. public interface PersonInfoService extends PersonSexSerivice {
  8. String getPersonName(int personId);
  9. int getPersonAge(int personId);
  10. }
  11. public interface PersonSexSerivice{
  12. boolean isMan(int personId);
  13. }

同样的,假如你需要判断一个人的性别,针对第二个要求: 类间的依赖应该建立在最小的接口上,所以仅需要注入PersonSexService即可。

  1. // 仅需要获取用户性别,最小依赖 PersonSexService
  2. PersonSexService sexService = new PersonInfoServiceImpl();
  3. boolean isMan1 = sexService.isMan(1);
  4. // 需要获取其他信息,需要依赖 PersonInfoService
  5. PersonInfoService infoService = new PersonInfoServiceImpl();
  6. boolean isMan2 = infoService.isMan(1);
  7. int personAge = infoService.getPersonAge(1);
  8. String personName = infoService.getPersonName(1);

4.1 星探搜索代码

假设我们需要实现星探搜索美女的一个软件,评判一个人是否是美女通常需要有一些条件: 好看的外观条件,良好的文化修养 以及 身材,依据此我们可以实现一个PrettyGirl的服务。

  1. public interface IPrettyGirl {
  2. void goodLooking();
  3. void nickFigure();
  4. void hasCulture();
  5. }

那么依据此我们可以实现简单的类图如下:

4、接口隔离原则(ISP) - 图1

上面的类图可以非常方便的展示出,类之间的关系,但是随着时代的发展,文化修养可能产出文化美女,但是我们的代码设计却还是以这三个条件作为判断依据,显然不是合适的,所以我们需要将文件修养单独实现该接口。在AbstractSearch的接口中分别实现这些判断逻辑,类图结构如下:

4、接口隔离原则(ISP) - 图2 这样以后需要文化性美女也可以很好地减少修改,有读者可能提出: 如果后面只需要好看的美女呢,是不是还要单独分离一个新的接口,确实如此,但是在有限的开发设计中,不能无限考虑未来变更的情况,否则会陷入设计的泥潭中而不能自拔。

4.2 最佳实践

  • 尽量保证接口最小化,即首先保证单一职责,然后保证接口隔离,不能出现臃肿接口(FatInterface)
  • 一个接口通常只维护一个服务或者模块
  • 接口需要高内聚,所谓的高内聚指的是 public 方法尽少的对外暴露,代码逻辑在类内部实现,对外承诺的服务越少,接口越安全
  • 定制化服务,同高内聚类似,对外暴露的接口尽可能的少,如果有不同的服务实现,尽量拆分服务
  • 切勿盲从,每个项目对于接口隔离的控制粒度都不一致,这需要我们不断的学习掌握

彻底贯彻接口隔离的方法是每个接口均有一个方法,但这样显然不合理,因此掌握每个接口的隔离粒度,就需要开发这不断地摸索掌握