基于接口而非实现原则

76 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第18天,点击查看活动详情

抽象类

继承的目的是为了代码的复用,那么抽象类的特性是只能被继承不能new,抽象方法子类必须重写提醒提前至编译器。

那么可看出抽象类的目的也是为了代码复用,但是除此之外还有很多用处。比如可以实现多态,使用父类的类型调用方法,这个方法在不同的子类中有不同的实现。

当然也可以手动去父类中定义空方法,在子类中重写方法,但是有以下几个缺点:

1.可读性差,父类中定义空方法,子类中去重写。父类为什么定义空方法?很难理解

2.子类中忘记重写了那个空方法出错

3.不使用抽象类,因此父类可以new实例化,但是是空方法,也会造成误用。,因为没有实现什么都没做。虽然可以使用私有构造方法但是总是没有那么优雅

抽象类更多的是为了代码复用,而接口就更侧重于解耦。调用者只需要关注抽象的接口,不需要了解具体的实现,具体的实现代码对调用者透明。接口实现了约定和实现相分离,可以降低代码间的耦合性,提高代码的可扩展性。

用接口的定义,理清何种程度的抽象类可以作为接口:1.没有成员变量 2. 只有方法声明,而没有方法实现 3.所有方法强制必须要被子类实现

如何选择?

如果我们要表示一种 is-a 的关系,并且是为了解决代码复用的问题,我们就用抽象类;如果我们要表示一种 has-a 关系,并且是为了解决抽象而非代码复用的问题,那我们就可以使用接口。

从类的继承层次上来看,抽象类是一种自下而上的设计思路,先有子类的代码重复,然后再抽象成上层的父类(也就是抽象类)。而接口正好相反,它是一种自上而下的设计思路。我们在编程的时候,一般都是先设计接口,再去考虑具体的实现。

基于接口而非实现编程

  • 函数的命名不能暴露任何实现细节。比如,前面提到的 uploadToAliyun() 就不符合要求,应该改为去掉 aliyun 这样的字眼,改为更加抽象的命名方式,比如:upload()。
  • 封装具体的实现细节。比如,跟阿里云相关的特殊上传(或下载)流程不应该暴露给调用者。我们对上传(或下载)流程进行封装,对外提供一个包裹所有上传(或下载)细节的方法,给调用者使用。
  • 为实现类定义抽象的接口。具体的实现类都依赖统一的接口定义,遵从一致的上传功能协议。使用者依赖接口,而不是具体的实现类来编程。

在定义接口的时候,不要暴露任何实现细节。接口的定义只表明做什么,而不是怎么做。 而且,在设计接口的时候,我们要多思考一下,这样的接口设计是否足够通用,是否能够做到在替换具体的接口实现的时候,不需要任何接口定义的改动。