2.1 场 景 问 题

大家都知道,在Java应用开发中,要“面向接口编程”。

那么什么是接口?接口有什么作用?接口如何使用?我们一起来回顾一下。

2.1.1 接口回顾

1.Java中接口的概念

在Java中接口是一种特殊的抽象类,跟一般的抽象类相比,接口里面的所有方法都是抽象方法,接口里面的所有属性都是常量。也就是说,接口里面只有方法定义而没有任何方法实现。

2.接口用来干什么

通常用接口来定义实现类的外观,也就是实现类的行为定义,用来约束实现类的行为。接口就相当于一份契约,根据外部应用需要的功能,约定了实现类应该要实现的功能,但是具体的实现类除了实现接口约定的功能外,还可以根据需要实现其他一些功能,这是允许的,也就是说实现类的功能包含但不仅限于接口约束的功能。

通过使用接口,可以实现不相关类的相同行为,而不需考虑这些类之间的层次关系,接口就是实现类对外的外观。

3.接口的思想

根据接口的作用和用途,浓缩下来,接口的思想就是“封装隔离”。

通常提到的封装是指对数据的封装,但是这里的封装是指“对被隔离体的行为的封装”,或者是“对被隔离体的职责的封装”;而隔离指的是外部调用和内部实现,外部调用只能通过接口进行调用,外部调用是不知道内部具体实现的,也就是说外部调用和内部实现是被接口隔离开的。

4.使用接口的好处

由于外部调用和内部实现被接口隔离开了,那么只要接口不变,内部实现的变化就不会影响到外部应用,从而使得系统更灵活,具有更好的扩展性和可维护性,这也就是所谓“接口是系统可插拔性的保证”这句话的意思。

5.接口和抽象类的选择

既然接口是一种特殊的抽象类,那么在开发中,何时选用接口?何时选用抽象类呢?

对于它们的选择,在开发中是一个很重要的问题,特别总结两句话给大家:

■ 优先选用接口

■ 在既要定义子类的行为,又要为子类提供公共的功能时应选择抽象类。

2.1.2 面向接口编程

面向接口编程是Java编程中的一个重要原则。

在Java程序设计里面,非常讲究层的划分和模块的划分。通常按照三层来划分Java程序,分别是表现层、逻辑层和数据层,它们之间都要通过接口来通信。

在每一个层里面,又有很多个小模块,每个小模块对外则是一个整体,所以一个模块对外应该提供接口,其他地方需要使用到这个模块的功能时,可以通过此接口来进行调用。这也就是常说的“接口是被其隔离部分的外观”。基本的三层结构如图2.1所示。

图片

图2.1 基本的三层结构示意图

在一个层内部的各个模块间的交互要通过接口,如图2.2所示。

图片

图2.2 一个层内部的各个模块间的交互示意图

各个部分的接口具体应该如何去定义,具体的内容是什么,我们不去深究,那是需要具体问题具体分析的,这里仅学习设计的方法。

上面频频提到“组件”,那么什么是组件呢?

所谓组件:从设计上讲,组件就是能完成一定功能的封装体。小到一个类,大到一个系统,都可以称为组件,因为一个小系统放到更大的系统里面去,也就当个组件而已。事实上,从设计的角度看,系统、子系统、模块、组件等说的其实是同一回事情,都是完成一定功能的封装体,只不过功能多少不同而已。

继续刚才的思路,大家会发现,不管是一层还是一个模块或者一个组件,都是一个被接口隔离的整体,那么下面我们就不去区分它们,统一认为它们都是接口隔离体即可,如图2.3所示。

图片

图2.3 接口隔离体示意图

既然在Java中需要面向接口编程,那么在程序中到底如何使用接口,来做到真正的面向接口编程呢?

2.1.3 不用模式的解决方案

回忆一下,以前是如何使用接口的呢,假设有一个接口叫Api,然后有一个实现类Impl实现了它,在客户端怎么用这个接口呢?

通常都是在客户端创建一个Impl的实例,把它赋值给一个Api接口类型的变量,然后客户端就可以通过这个变量来操作接口的功能了,此时具体的结构图如图2.4所示。

图片

图2.4 基本的接口和实现

用代码来说明会更清楚一些。

(1)首先定义接口Api,示例代码如下:

13243caf6a6b4e2b98f7f6ab4473517b

(2)既然有了接口,自然就要有实现,定义实现Impl,示例代码如下:

6f5ea5f78f304d93b131df03c61c6ed8

(3)那么此时的客户端怎么写呢?

按照Java的知识,接口不能直接使用,需要使用接口的实现类,示例代码如下:

4467fcfc9d7c4162ad2afcfa24039c1a

2.1.4 有何问题

上面写得没错吧,在Java的基础知识里面就是这么学的,难道这有什么问题吗?请仔细看位于客户端的下面这句话:

Api api=new Impl();

提示

然后再想想接口的功能和思想,发现什么了?仔细再想想?

你会发现在客户端调用的时候,客户端不但知道了接口,同时还知道了具体的实现就是Impl。接口的思想是“封装隔离”,而实现类Impl应该是被接口Api封装并同客户端隔离开的,也就是说,客户端根本就不应该知道具体的实现类是Impl。

有朋友说,那好,我就把Impl从客户端拿掉,让Api真正地对实现进行“封装隔离”,然后我们继续面向接口来编程。可是,新的问题出现了,当他把“new Impl()”去掉后,却发现无法得到Api接口对象了,怎么办呢?

把这个问题描述一下:在Java编程中,出现只知接口而不知实现,该怎么办?

就像现在的Client,它知道要使用Api接口,但是不知由谁实现,也不知道如何实现,从而得不到接口对象,就无法使用接口,该怎么办呢?