java接口和抽象类的区别,什么时候该用接口什么时候该用抽象类(彻底搞懂!!!)
很多人在面试过程中都会被问到这个问题,语法上的区别大家都知道,但是如果面试时你只说语法上的区别,显然不合面试官的胃口
本文重点关注这两者在设计方面的区别:
接口大家比较熟悉一点,因为现在的MVC架构业务层规范就是先写接口再写实现类,那大家有没有想过,用抽象类代替这些接口,是否可行呢?从语法上当然是可行的,但是不会这样做,为什么呢?大材小用…
举个例子,领导说了,需要有个订单报表,可以导出excel,于是你定义了一个接口和一个实现类:
此时接口就可以完全胜任,无需动用抽象类。程序正常运行,报表导出OK,一切都很完美
后来,领导又说了,商品报表也需要导出,你的第一反应是下面这样嘛?o(╥﹏╥)o
或者是这样?
后面领导又说了,还得加N个报表,并且每种报表都要支持日报、月报、季报、年报…,都用这种方式,能实现嘛?当然可以,但是很明显,一点也不优雅,因为有重复!重复是我们代码最容易出现,也最容易被忽视的坏味道!我们从上面几张图可以很明显看出重复部分,即:
不同点就在于数据统计,其他部分逻辑重复,是可以抽出来的。注意了:抽象类的场景出来了,那就是多态+复用!!我们的类要做一件事情,这件事情分为几个步骤,有些步骤是固定的(复用),有些步骤需要根据不同情况有不同实现(多态),这时候接口就满足不了要求了,因为接口只能满足多态,那我们就应该想到抽象类,我们先看代码,再对其进行介绍:
可以看到,报表导出方法分为四步,被抽成四个方法,有三个公共方法,一个抽象方法为查询报表数据,由于不同的报表,数据查询逻辑不一样,所以它应该被作为抽象方法,由子类去实现。子类只需要实现该抽象方法即可,比如订单子类就去查询并统计订单库,商品子类就去查询并统计商品库:
如此,不管加什么报表,只需要实现这个数据查询方法即可,显然比仅仅实现接口方式要优雅很多。
我们可以看到抽象类就相当于一个模板,模板中有子类可以公用的部分,也有需要子类自行实现的部分,是为模板式设计,它同时具有多态性+复用性;
而接口是对行为的抽象,它只定义一组行为规范,每个实现类都要实现所有规范,叫辐射式设计,它只有多态性;
其实,在实际开发过程中,接口和抽象类并不一定是上面例子中非此即彼的情况,在遇到和上述例子中类似情况时,一般来说,会有这样一个演进的过程(面试时,你可以直接把这个过程讲给面试官,比背他们的区别要来的简单有效):
1.首先是设计接口IService,定义行为service();
2.然后是接口实现,你会发现多个实现类中干了一些重复的事情,这时我们再设计一个抽象类AbstractService
3.AbstractService中定义同样的service()方法,将上一步发现的重复内容提取到该方法中
4.对于第2步的多个实现类来说,现在重复的部分已经被提取了,剩下的就只有各自特殊的部分了,我们再在抽象类中为特殊的部分定义一个抽象的方法doService()
5.将第2步的多个实现类去掉implements接口,改为继承抽象类,实现doService()方法,去掉重复部分的内容,只留下各自特殊的部分
6.接下来,我们要考虑接口还有没有存在的必要了,如果你觉得现在或未来可能有另一种实现类,它并没有和其他实现类重复的部分,那就留下来,让抽象类实现这个接口,那继承体系就是这样:
如果,你觉得不会存在这种情况,那就去掉这个接口,继承体系就是这样: