java中接口有什么作用?

说一下我的个人理解。

参考:https://www.zhihu.com/question/20111251

 

一、买东西的例子

感谢知乎网友梅云罗的回答,我自己做了一些修改

Buy.java

Buy在这里是一种行为的规范。如果想要买东西(使用buy()方法),那就得实现Buy接口。Dog实现类实现了Buy接口,说明Dog可以buy。同样,Cat实现类实现了Buy接口,说明Cat也可以buy。而且,Dog和Cat要买的东西可以不一样(Dog要买骨头,Cat要买鱼,也就是说接口实现的过程可以不一样),接口只关心实现类的规范,而不关心实现方法。

Shop.java

实现了一个Shop,Shop就是提供Buy这个行为的地方,只要是来买东西的,Shop都欢迎(public Shop(Buy Buy))。如果Dog来了,Dog是来买东西的,那么就可以进Shop,买自己要的东西(Buy.buy())。如果Cat来了,Cat也是来买东西的,那么也可以进Shop,买自己要的东西(Buy.buy())。

这样写的好处在哪里呢?这个Shop欢迎所有来买东西的顾客,无论是Dog还是Cat,只要是来买东西的(实现了Buy接口),都可以进来。那我就不需要为Dog单独建一个Shop,Cat也单独建一个Shop了。

如果明天来了一只Tiger也想买东西,怎么办?如果Tiger就是想搞特权,想要vipBuy(新的vipBuy()方法),那就不能实现Buy接口了。同时,Tiger也不能去普通的Shop,只能去建一个vipShop,专门给Tiger提供服务。

也就是说,如果Tiger的方法是与众不同的,那当然只能单独为Tiger提供服务啦。

我的理解:

Shop就是提供Buy服务的(public Shop(Buy Buy)),只要你是来买东西的(实现Buy接口),就可以进来(遵守接口规范),随便你怎么买(接口不关心实现类怎么实现)。如果你非要搞特权(不实现Buy接口),那我就不能放你进来了(不继承接口,不成为实现类,就无法使用接口中的方法),你去别的vipShop吧(实现别的vipBuy接口或者单独新建一个vipShop,单纯为Tiger提供服务)。

所以使用接口就让程序更加简单直观了。我知道我想买东西,那就实现Buy接口。至于怎么买,倒立着买,旋转着买,这都是你的自由啊(实现类中的buy方法是自定义的),接口根本就不管。这样,提供这个服务的Shop就好进行管理了。只要是买东西,就可以来。而不是每来一种新顾客,就要专门去新建一个Shop。

这里还涉及一个松耦合的问题。同一个Shop,只要是买东西的,Dog可以来,Cat也可以来。就像是如下的场景:我想获得数据库链接对象,只要是提供数据库链接的,都可以来。今天我用的是Mysql,那么Mysql的链接实现类可以来。明天我想换用Oracle,那么Mysql实现类不可以再使用,我当然希望Oracle的实现类也可以来啦。所以,Mysql和Oracle这两个实现类只要实现同一个接口就可以了。

二、Comparable的例子

感谢知乎网友Dion的回答

能问这个问题,说明(1)你很会思考(2)编程水平还是处于起步阶段。

“接口是个规范”,这句没错。

“不如直接就在这个类中写实现方法岂不是更便捷”,你怎么保证这个接口就一个类去实现呢?如果多个类去实现同一个接口,程序怎么知道他们是有关联的呢?

既然不是一个类去实现,那就是有很多地方有用到,大家需要统一标准。

甚至有的编程语言(Object-C)已经不把接口叫 interface,直接叫 protocol。

统一标准的目的,是大家都知道这个是做什么的,但是具体不用知道具体怎么做。

比如说:我知道 Comparable 这个接口是用来比较两个对象的,那么如何去比较呢?

数字有数字的比较方法,字符串有字符串的比较方法,学生(自己定义的类)也有自己的比较方法。

然后,在另外一个负责对象排序(不一定是数字喔)的代码里面,肯定需要将两个对象比较。这两个对象是什么类型呢?Object a,b?肯定不行,a > b 这样的语法无法通过编译。int a,b?也不行?一开始就说了,不一定是数字。

….

所以,Comparable 就来了。他告诉编译器,a b 两个对象都满足 Comparable 接口,也就是他们是可以进行比较的。具体怎么比较,这段程序不需要知道。所以,他需要一些具体的实现,Comparable 接口有一个方法,叫 compareTo。那么这个方法就是用来取代 <、> 这样的运算符。

因为运算符是编译器保留给内置类型(整数、浮点数)进行比较用的,而不是一个广义的比较运算。

如果你可以明白 JDK 自身库里面诸如 Comparable 这样已经有的接口,那么就很容易理解自己在开发程序的时候为什么需要用到接口了。

我的理解:

看了一下compare方法:

属于java.lang.Comparable包,默认是比较string的:

如果需要比较其他类型,可以实现Comparable接口去重写compare即可。

感觉这个例子很好。特别是“统一标准的目的,是大家都知道这个是做什么的,但是具体不用知道具体怎么做”,总结得很精辟。

三、接口的一些意义

感谢知乎网友暗灭的回答,我自己做了一些修改

1.实现接口分离

“接口+实现”最常见的优势就是实现类和接口分离,在更换实现类的时候,不用更换接口功能。

2.方便做单元测试(针对使用spring的情况)

为什么对单元测试很方便?

一般而言会用Spring配置Bean,所以实际上你的单元测试代码也不用有改动,无论是测试哪一个实现类,都只通过更改配置文件就可以完成。

想想,如果没有接口呢?是不是要对每一个短信通道单独写一个单元测试的方法?

3.对于不需要频繁更变实现类的方法,是不是就可以不用写接口了?

答案是No。整个系统架构的代码可以单纯认为有四部分构成。

Model+Interface+Service+Util

Model是纯粹的Pojo,Inteface和Service是接口和实现分开的,Util是全项目通用,或者是跨项目通用的,跟业务逻辑没有任何关系的。

写接口最大的好处就是在你写的Controller代码,或者是Service里的主要业务逻辑代码的时候,屏蔽掉细节。

写一个业务逻辑的时候,比如说的加入班级。第一步,做校验,用户是否为空,班级是否不存在,是否已经加入了班级等等。第二步,更新班级和用户的关系表,更新班级总人数,更新职业总人数,更新用户的最新班级ID。第三步,发送系统通知,告知用户加入班级成功。

如果说不用接口,只用实现类的话,第一种方式就是把所有的代码都写在这个Controller里去,代码会非常非常繁琐,一个函数突破几千行轻轻松松,而且改动起来很麻烦。

第二种方式就是抽象出来函数。

这种方式在某种程度上能够解决代码块大的问题,但是你必须要New一个实现类出来,想想在上述逻辑中,需要new几个实现类?这些实现类就会被New的各处都是,甚至改个名字都很蛋疼。

但是如果你使用接口的话,你会发现,接口是强制于你去将复杂的业务逻辑抽象成具体做的事儿。

比如说,if(user==null){ // to do something}就变成了CheckUser(uid)这么一个接口。实现类也明确了自已要做的事情。

从某种程度上来说,抽象成一个私有方法也能解决这个问题,但是一般都会推荐,如果你发现你写了很多私有方法,要么是他们可以继续演化成一个util,要么是可以成为一个Service。

我的理解:

特别是第三点解决了我的之前的疑问:为什么不把所有代码都写在一个controller中。接口可以让项目结构更加合理。

四、总结

之前曾经想过接口意义的问题。今天看了这么多回答之后,感觉有了更深的理解。还是要多写代码,多去维护项目,才能切身体会到接口的优点。

发表评论

电子邮件地址不会被公开。 必填项已用*标注