likes
comments
collection
share

04 每个类都需要都定义接口吗?

作者站长头像
站长
· 阅读数 13

上一节我学习了,接口和抽象类,抽象类和继承的区别以及对于多态的重要性。为什么要多用多组合,少用继承,那么在实际编程中,领导让我实现一个模块的功能,那我怎么样在设计的时候知道该不该用接口呢?

1.工程中实现接口的意义

首先,需要搞清楚接口的工程意义。从本质上来看,“接口”就是一组“协议”或者“约定”,是功能提供者提供给使用者的一个“功能列表”。日常大家都听过的,就是基于接口编程,其实如果不在工程中实际使用,仅仅是面试背诵八股文,那么基于接口编程,仅仅是六字箴言。前一段时间做了一个功能模块,通过实践了解了什么叫基于接口编程。

04 每个类都需要都定义接口吗?

根据上面的例子,学生只关注去学校,不关注用那种交通方式去,这样即使交通方式改变,也不会影响学生侧的代码逻辑实现。这样就将接口和实现相分离,封装不稳定的实现,暴露稳定的接口。上游系统面向接口而非实现编程,不依赖不稳定的实现细节,这样当实现发生变化的时候,上游系统的代码基本上不需要做改动,以此来降低耦合性,提高扩展性。

在工程中,我们需要考虑的最低级的情况就是实现功能,而高级工程师就是考虑兼容需求的各种变化,而对于未来需求的变化出现的不同的架构和方案,也就是每个工程师能力的体现。

越抽象、越顶层、越脱离具体某一实现的设计,越能提高代码的灵活性,越能应对未来的需求变化。好的代码设计,不仅能应对当下的需求,而且在将来需求发生变化的时候,仍然能够在不破坏原有代码设计的情况下灵活应对。而抽象就是提高代码扩展性、灵活性、可维护性最有效的手段之一

2.实际中怎么应用呢?

刚刚开始我们业务是用firebase(类似于发送短信的工具,用于回收数据到业务)打点,用来给产品回收数据。具体实现代码如下:

public class FirebaseReporter {
 //... 省略属性、构造函数等...
 
 public void initIfNotExisting() {
 // ... 创建 bucket 代码逻辑...
 // ... 失败会抛出异常..
 }
 
 public String vaildFirebaseScreet() {
 // ... 密钥验证
 }
 
 public String reportDataToFirebase(String page, String id){
 //... 打点到firebase..
 }
 
}

代码功能已经实现了,现在看来没什么问题,初始化,创建链接,打点。但是后续我们产品觉得firebase打点时效性太低了,决定使用实时性打点Pubsub。那么问题来了我们需要完全重新搬写一套,其实这只是打点的一部分,整个项目代码量很大,需要逐一查找修改。如果Friebase和Pubsub都是一个人接入还好,比较熟悉业务,假如是一个新人,那么无疑会带来很多安全隐患,特别是数据有一些时候会影响方向,并且难以发现。

那这两个问题该如何解决呢?

解决这个问题的根本方法就是,在编写代码的时候,要遵从“基于接口而非实现编程”的原则,具体来讲,我们需要做到下面几点:

  1. 函数的命名不能暴露任何实现细节。比如,前面提到的 FirebaseReporter() 就不符合要求,应该改为去掉 Firebase 这样的字眼,改为更加抽象的命名方式,比如:Reporter()。

  2. 封装具体的实现细节。比如vaildFirebaseScreet验证密码不应该使用Public 并且不应该使用这么明确的命名。

  3. 为实现类定义抽象的接口。具体的实现类都依赖统一的接口定义,遵从一致的上传功能协议。使用者依赖接口,而不是具体的实现类来编程,比如说上报页面,上报点击等等,不应该继续使用reportDataToFirebase()这种无法通用的命名。

总结一下,我们在做软件开发的时候,一定要有抽象意识、封装意识、接口意识。在定义接口的时候,不要暴露任何实现细节。接口的定义只表明做什么,而不是怎么做。而且,在设计接口的时候,我们要多思考一下,这样的接口设计是否足够通用,是否能够做到在替换具体的接口实现的时候,不需要任何接口定义的改动。

这样,新来的小伙便能出色的完成新的数据打点的工作,你就能早点滚蛋了,哈哈哈哈哈。

3.是否需要为每个类定义接口?

1和2讲了意义跟实现,这些网上都可以百度到,个人觉得最重要的还是这一条,仔细理解了这一条,我们就能在实际项目中知道是否该用接口,也就提高了代码的抽象程度和可扩展程度。

做任何事情都要讲求一个“度”,过度使用这条原则,非得给每个类都定义接口,接口满天飞,也会导致不必要的开发负担。什么时候不需要定义接口,直接使用实现类编程,我们做权衡的根本依据,还是要回归 到设计原则诞生的初衷上来。或者你仔细看了我再2中的例子,你或许也能理解接口的使用场景。

这条原则的设计初衷是,将接口和实现相分离,封装不稳定的实现,暴露稳定的接口。上游系统面向接口而非实现编程,不依赖不稳定的实现细节,这样当实现发生变化的时候,上游系统的代码基本上不需要做改动,以此来降低代码间的耦合性,提高代码的扩展性。

这个十分重要,在编写代码的过程中,这一点既考验你的编程素养,也考验你对于项目的熟悉程度,到底这个模块是稳定还是要经常变动的? 如果在我们的业务场景中,某个功能只有一种实现方式,未来也不可能被其他实现方式替换,那我们就没有必要为其设计接口,也没有必要基于接口编程,直接使用实现类就可以了。

写完这一篇文章后,我又重温了一遍,什么时候改用接口,接口的好处,以及怎么抽象接口的思想。

转载自:https://juejin.cn/post/7306459858126635058
评论
请登录