一篇带你入门面向接口编程

文章的开头我们先来复习一下面向对象编程的几大特性:封装、继承、多态、抽象。而面向接口编程是面向对象编程的一个重要方面。它是上面提到的面向对象的四大特性和一些设计模式的基础。然而并不是所有的编程语言都支持接口这一特性。在不同的语言中对于抽象类和接口的支持程度也各有不同。本文会首先介绍接口的使用意义,然后介绍接口与抽象类的区别和联系,最后再介绍C++和JAVA两大主流语言中对于接口和抽象类的支持情况。

1.接口的意义

很多同学在一开始学习接口的时候并不明白为什么要有接口,教科书上的语言过于抽象以至于让人费解,案例又过于短小以至于不能体现接口的意义。我在最开始学习接口的时候总是不知道如何使用它(当时没有接触过正经的项目),甚至于一度觉得不用接口不也可以实现功能吗?那么接口的价值体现在哪里呢?

本文重点介绍接口的两大价值:1.将协议标准与行为解耦,对调用者而言可以屏蔽底层的实现。2.定义好一种标准,使框架/系统更加易于扩展。第二点可以理解为第一点的补充。

举一个生活中最常用的关于接口的例子:USB接口。有人说你怎么举例的是一种硬件的接口呀?其实硬件的接口和我们代码里面的接口的含义是相似的,完全可以借助硬件上我们熟悉的接口来理解面向对象编程中的接口。

百度百科上对于USB是这样定义的:“USB,是英文Universal Serial Bus(通用串行总线)的缩写,是一个外部总线标准,用于规范电脑与外部设备的连接和通讯。“这里我们可以看到,USB首先是一套标准。也就是说,接口它是定义了一种标准。甭管你是U盘还是硬盘,是机械硬盘还是SSD,只要您想通过USB与电脑通讯,您都要遵守并实现一套USB协议。那对于电脑(调用者)来说,电脑不用关注U盘读取数据和硬盘读取数据的具体区别,它只需要按照USB接口这套标准来调用数据的提供者进行读取数据即可。这样就把协议标准与行为进行了解耦,对调用者而言可以屏蔽底层的实现。

回到软件开发上,我们提供接口供别人调用的时候,也是起到了解耦的作用。调用者无需关注实现的算法和数据结构是什么,只要你知道我是符合这个接口的,接口里面定义的功能我都有就行了。所以我们更倾向于接口是表达了一种has-a的关系。这里再举一个java开发里面常见的例子:

我们都知道List 是一个接口,它继承于Collection的接口。List接口的实现有很多,包括:ArrayList,LinkedList,Vector,Stack等等。假如ArrayList和LinkedList不使用面向接口编程,即它们没有都去实现List接口。那么我们使用的时候就会这样(用两个方法分别处理ArrayList和LinkedList):

deal(ArrayList arrayList) {
arrayList.add();
// 此处省略若干对于list的调用
}

deal(LinkedList linkedList) {
linkedList.add();
// 此处省略若干对于list的调用
}

但是幸运的是,jdk的代码是面向接口的,ArrayList和LinkedList都实现了List接口,那么我们只需要真的List这个类型定义一个方法即可处理所有实现了List接口的类型:

deal(List list) {
list.add();
// 此处省略若干对于list的调用
}

显然在后者中,我们作为调用者无需关注ArrayList 和LinkedList 的区别了,只需要知道它们都实现了LIst接口就行。

我们再来看接口的第二点意义:定义好标准,提高系统的扩展性。

我们以Spring框架中InitializingBean接口为例。这个接口是Spring框架为它的使用者提供的一个扩展点,如果使用Spring框架进行开发的开发者,希望在初始化Bean的时候可以实现自定义的功能,那么就应该实现这个接口。换言之,InitializingBean接口为bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是继承该接口的类,在初始化bean的时候都会执行该方法。

好的框架需要给使用者预留恰到好处的扩展功能,支持使用者进行一些自定义的设置。但是前提是框架必须定义好这个扩展点(往往是用接口定义的,因为扩展必须遵守一定的标准)。这就好像一个U盘插到USB接口之后再开机,那么机器是可以识别到这个U盘的。因为机器开机的时候就会去检查这些接口,然后加载。但是如果机器预留的是一个USB接口结果使用者把一个TYPE C接口接入到了USB接口上(不进行任何适配和转换的情况下),那么肯定是识别不了的。简单说就是我作为框架允许你扩展,但是你的扩展必须遵守我定义好的标准。

2.接口与抽象类有哪些区别和联系?

乍一看接口和抽象类的功能都是抽象,好像两者差不多,然而实际上并非如此。

我们先来看抽象类和接口的作用,本质上就是有所不同的。

抽象类是一种类,本质上是为了代码复用(牛逼的用法是利用多态的代码复用)。抽象类表达的是继承关系,是is-a的关系。而接口是为了解耦,表达的是一种契约/约定/协议/标准,是has-a 的关系。

他俩的功能和意义就是完全不同的。了解了两者的根本不同之后,我们再来比较其他的特点。

首先,抽象类和接口都不可以被直接实例化。

其次,抽象类中允许有成员变量,接口中不允许,接口中只允许存在静态变量。

再次,抽象类中允许有包含代码实现的方法,但是接口中不允许。接口中所有的方法必须都是抽象的。

最后,抽象类的实现类可以只实现抽象类中的抽象方法,但接口的实现类需要实现接口的所有方法(java8中允许在接口中使用default关键字定义方法,这种方法可以不在实现类中实现)。

3.C++和JAVA两大主流语言中对于接口和抽象类的支持

JAVA支持接口(Interface关键字)和抽象类(abstract关键字),但是C++中,接口就是一种特殊的抽象类。虽然编程语言对于接口和抽象类的支持程度不尽相同,但是作为开发者仍然可以也应该有面向接口编程的思想。

关于接口和抽象类的基本知识就先聊到这里,欢迎你在评论区留言你的意见和建议!后续将会推出文章谈一下java底层是如何支持多态这一特性的,如果感兴趣请关注我的公众号!