1.引子
在我们日常开发中,比如说我们熟悉用java编程语言开发项目,经常会使用到接口与抽象类,我们还经常说要基于接口而非实现编程。
- 那么你知道为什么要这么做吗?这样做能带来哪些收益?
- 那么你知道什么是接口?什么是抽象类吗?
- 那么你知道接口与抽象类有什么区别吗?什么时候该用接口,什么时候该用抽象类吗?
带着这些问题,让我们开始今天的分享。
2.关于接口
2.1.接口定义
首先让我们来看接口,关于什么是接口?我想请你先简单回顾一下接口的定义。在java编程语言中,我们对接口是这么定义的。
- 定义一个接口,需要使用interface关键字
- 接口中不可以有实现的方法,即接口中的方法都是抽象方法,否则编译会报错。当然在jdk8以后,接口中允许有实现的方法,通过default关键字修饰
- 实现一个接口,需要使用implements关键字,子类实现接口,必须实现接口中的所有抽象方法,一个子类支持实现多个接口,
- 接口不能实例化,即不能通过new创建接口对象
2.2.使用接口的收益
知道了接口的定义,我们来看在实际项目中,我们为什么要使用接口,它能带来哪些收益?基于接口而非实现编程,给我们直观带来的收益有。
- 易用性,面向对象编程四大特性中,你还记得抽象特性吗?抽象支持向使用者隐藏实现细节。即使用者只需要关系接口提供者有什么能力,不需要关心提供者如何提供接口能力。让使用者以轻松,舒服的方式使用接口。而面向接口编程,即是支持抽象特性的一种语法机制
- 扩展性,这是基于接口而非实现编程的另一个收益。通过接口声明接口行为,通过子类实现行为细节,看到这句话,你能联想到面向对象编程四大特性中的多态了吗?它也是很多设计原则,设计模式实现的基础。比如说开闭原则,比如说策略设计模式
3.关于抽象类
3.1.抽象类定义
看完接口的定义,我们再来看关于什么是抽象类?我还是想请你先简单回顾一下抽象类的定义,在java编程语言中,我们对抽象类是这么定义的。
- 定义一个抽象类,需要使用abstract关键字
- 抽象类中,支持实现方法,也支持抽象方法
- 继承一个抽象类,需要使用extends关键字,子类继承抽象父类,必须重写父类中的所有抽象方法,且一个子类只能继承一个抽象父类,java只支持单根继承(这里我想稍微扩展一下,抛一个小问题:为什么java不支持多重继承,即一个子类继承自多个父亲呢?借助搜索引擎的力量,希望你可以搞清楚这个问题)
- 抽象类不能实例化,即不能通过new创建抽象类对象
3.2.使用抽象类收益
从抽象类的定义来看,我们发现抽象类与接口的定义相似度比较高,这是因为它们面对的是相同的问题域,面向对象编程四大特性中的抽象、多态。
但是细看,抽象类与接口之间还是存在比较大的差异的。
- 抽象类,强调的关键字是继承,接口强调的关键字是实现。你还记得面向对象编程四大特性中的继承特性,它主要解决的是什么问题吗?答案是复用性,即公共代码在父类实现一次,所有子类复用父类的方法
- 另外一个子类,只能继承一个父类,而一个子类,可以实现多个接口。这也是语法机制上的一个大的差异
通过以上分析,我们看到了使用抽象类,带来的最大收益是代码复用性。事实上你需要注意,继承这个事情它是一把双刃剑,通过继承获取代码复用性的同时,另外一边的成本是增加了代码的耦合性,以及降低了代码的可读性。
想象一下我们我们有一个子类,比如说D类,继承自C类,C类继承自B类,B类继承自A类。使用D类的时候,我们需要同时知道A/B/C/D四个类中都提供了什么能力,代码的复杂性增加,可读性降低。
再想象一下,一旦修改ABC中任意一个父类,必然会对D类产生影响,耦合性非常高。
基于以上两点,才有了基于接口而非实现编程,以及多用组合少用继承设计思想的最佳实现。
当然我们不是说不能用继承,不使用抽象类,都要用接口。在面对具体应用场景的时候,我们需要具体问题具体分析。
比如说如果我们业务场景的编程模型,就是要强调is-a的关系(继承关系),且继承的层次并不深,最大化了代码复用性的收益,此时带来的代码耦合性、可读性成本并不高,我们通过抽象类,通过继承方式实现是比较理想的实践方式。
那么如果我们的业务场景编程模型,强调的是has-a的关系(组合关系),带解的问题域是抽象而非代码复用性问题,通过接口实现是比较理想的实践方式。
好了,关于接口与抽象类,今天我们就简单分享到这里了,期望这篇文章能为你在实际开发中关于接口与抽象类的使用,带来一些帮助。