文章大纲
🐹 一、前言
🐣 二、面试官这样询问的用意
🐰 三、什么是接口
🐷 四、面向过程编程、面向对象编程、面向接口编程对比
🍅 4.1、面向过程编程(POP)
🍑 4.2、面向对象编程(OOP)
🍒 4.3、面向接口编码(IOP)
🐴 五、面向过程编程特点
🐓 六、面向对象编程特点
🐋 七、面向接口编程的作用
🍑 7.1、符合开闭、依赖倒置原则、增强拓展性
🐇 八、抽象类和接口的关系
🐈 九、标记接口
🐧 十、关于面向接口编程在实际运用的一些问题
10.1、为什么现在很多项目从controller、service到dao层都是使用: 接口 + 实现类的方式、但是只有一个实现类,这种方式是否有必要?
🐉 十一、写在最后
🐬 十二、参考资料
🐹 一、前言
大家好,我是小诚,又到了愉快的学习时间,最近收到小伙伴投稿,在面试的时候被面试官询问到一个比较少见的问题: 什么是面向接口编程? 说实话,之前我也面试过很多公司,也没有遇到过这个问题,感觉挺新颖的,故特以此文记录学习,因为本人水平有限,看待问题的时候可能不够全面,如有不同看法,欢迎大家在下方留言讨论。
如果文章对你有帮助,可以帮忙一键三连哦! 如面试中遇到一些奇怪或者比较新颖的题目,欢迎私信投稿,感谢阅读!
🐣 二、面试官这样询问的用意
面试官提问一个问题的时候,我们需要不是马上去答复,而是要揣摩出面试官提问的用意,这样才能够给出更全面的回答。关于这个问题,我个人猜想面试官的大概用意如下:
1、看你是否了解过面向接口编程(不用奇怪,很多人只是听说过,有个模糊的概念,叫他说并不一定能说清楚)
2、看你是否清楚面向接口编程的使用初衷(如果一个技术/思想出现没有解决存在的某些问题,那它存在的价值在哪里?)
3、看你是否真正的将面向接口编程运用到开发中
🐰 三、什么是接口
在介绍什么是面向接口编程之前,让我们先来认识下什么是接口,这样才能够"知其然,知其所以然"。
接口,英文名叫Interface,可以理解成是一种标准(规范),在广义泛指一组标准的集合,它规定了实现该接口的类或者接口必须也拥有这一组规则,当然存在一种特殊情况,即空接口(不存在任何方法的接口)后续会介绍。
在一个面向对象的系统中,系统之间的各项功能小到类之间,大到模块、系统之间的交互实际上是通过不同对象的相互协作来实现的,在设计阶段,并不会太关注内部的实现细节,而是着重于设计对象之间的协作关系,尽量达到高内聚、低耦合的目的。
接口作为实体抽象出来的一种表现形式,用于抽离内部实现进行外部沟通,最终实现内部变动而不影响外部与其他实现交互,可以理解成按照这种思想来设计编程的方式就可以称为面向接口编程(现在讲得比较抽象,下面通过具体的例子形象化)。
举个例子: 就像电脑上的USB插口,如果你觉的有线鼠标用着不过瘾,完全可以购买一个无线鼠标插入原来有线鼠标的插口即可实现无缝切换,而不需要修改计算机中的任何地方,无论你的无线鼠标是否和有线鼠标是同种类型,因为这个插口就是一种标准,只要你购买的鼠标实现了这个标准,它就可以通过这个插口使用对应的鼠标。
PS:USB,是英文Universal Serial Bus(通用串行总线)的缩写(不是你是傻x的缩写…),是一个外部总线标准,用于规范电脑与外部设备的连接和通讯。是应用在PC领域的接口技术。
🐷 四、面向过程编程、面向对象编程、面向接口编程对比
在编码过程中,会听到关于这三者的介绍,有时候虽然对它们的概念有大致的了解,但是让你表达出来的时候总感觉表达不明白,下面通过具体的例子更加形象直观对它们重新的认识吧。
🍅 4.1、面向过程编程(POP)
面向过程编程(Procedure Oriented Programming 简称POP :如C语言),着重的是过程,解决一个问题的时候,先分析出解决这个问题需要的步骤,然后使用函数将这些步骤一步步实现,然后处理问题的时候按照一定的顺序将这个函数一个个调用,方法执行完后问题也解决了。
举例说明:肚子痛、怎么解决?(这是一个有味道的例子,但是为了让大家理解,所以举例得更去贴近生活些,如果觉的味道太大,下方留言,下次换个好例子)
如果使用面向过程编程方式解决这个问题,那么流程大概如下:
1、执行查找纸巾方法(不找纸巾难道用手?)
2、执行坐上马桶方法(没有马桶你不会站着?)
3、执行清理人体食物残渣排泄物方法(哎,舒服!)
4、执行清洁方法(不清洁,难道上完号就提起裤子就走?)
5、执行冲马桶方法(不冲!臭的可是你自己!)
面向过程编程小结:
通过上面的例子会发现,面向过程编程的思想在解决问题时,是将问题拆分成一个个步骤,每个步骤封装成对应的函数,然后按照某个顺序去执行,从而解决问题。
🍑 4.2、面向对象编程(OOP)
面向对象编程(Object Oriented Programming 简称OOP :如C++,JAVA等语言),侧重点在对象,解决一个问题时,先将问题中的包含的事物抽象成对象概念,对象中包含具体的属性和行为,真正执行时再让每个对象去执行自己的某些方法,从而解决问题。
举例说明:肚子痛、怎么解决?
使用面向对象编程方式解决这个问题的大概流程如下:
1、根据问题涉及到的实体,抽象出“人”对象、”马桶“对象、”纸巾“对象
2、针对“人”对象添加一些属性和方法,属性如:xx 28cm、黑长直。方法:寻找纸巾方法、坐上马桶方法、清除排泄物方法、清洁xx工具方法
3、针对“马桶”对象添加一些属性和方法,属性如:白色、长15m、椭圆形。方法:冲马桶方法
4、针对“纸巾”对象添加一些属性和方法,属性如:白/黑/金色
5、执行:
人对象.寻找纸巾方法
人对象.坐上马桶方法
人对象.清除排泄物方法
马桶.冲马桶方法(是的,马桶是自感应的,有钱你也可以这样玩,有钱人的生活就是舒服)
人对象.清洁xx工具方法(清洁哪里不用我说你也懂吧!DDDD)
面向对象编程小结:
通过例子可以发现,解决相同的问题,面向对象编程的方式是先将问题中的实体抽象成具体的对象,然后再将属性和方法封装到对象中,最后通过不同的对象执行相应的方法解决问题。
🍒 4.3、面向接口编码(IOP)
面向接口编程(Interface Oriented Programming:OIP)是一种编程思想,接口作为实体抽象出来的一种表现形式,用于抽离内部实现进行外部沟通,最终实现内部变动而不影响外部与其他实现交互,可以理解成按照这种思想来设计编程的方式就可以称为面向接口编程。
它并不是比面向对象编程更先进的一种独立的编程思想,可以说属于面向对象思想体系的一部分或者说它是面向对象编程体系中的思想精髓之一。
注: 上图中红框标出来的地方就是区别于面向对象编程的一些特点。
上面面向过程和面向对象都举例肚子痛问题解决的例子,结合我们平常生活中经验可以知道,无论男女、都可能出现肚子痛需要上厕所的问题,但是不同的人有不同的方式解决这个问题,不可能每有一个不同处理方式的人就修改一次原来代码,这样不仅不符合面向对象编程中的开闭原则【对拓展开放,对修改关闭】,还可能带来潜在的风险。
因此、我们可以将上面面向对象编程中上厕所的五个步骤抽取到【人肚子痛处理接口]和】【马桶接口】,哪个人肚子痛就实现这个【人肚子痛处理接口】,每个马桶类型都实现【马桶接口】(因为不是每个马桶的清理细节都一样,有的是自动,有的是手动)。
具体怎么上厕所、冲马桶由你自己定义,你可以站着上、坐着上、倒立上都可以,这样就达到了内部变动而不影响外部交互的目的,我使用这个接口类型接收实现了这个接口的实现者,实现者中的方法逻辑修改了,并不影响我接收它,进行方法调用。
🐴 五、面向过程编程特点
优点:
将问题拆解成一个个步骤、类似流水线一样,一步步执行,将复杂的问题流程化进而简单化。
性能比面向对象编程高,面向对象编程中类调用过程需要加载、实例化,资源消耗更大;对向性能要求高的比如单片机、嵌入式开发、 Linux/Unix等一般采用面向过程开发。
缺点:
没有面向对象编程易维护、易复用、易扩展
使用场景:
适用于性能要求较高的系统开发中
🐓 六、面向对象编程特点
优点:
易理解:采用面向对象思想设计开发,更符合人的思考方式,可读性高。
易维护:面向对象有封装、继承、多态性的特性,即使需求有变动,需要维护的更多是局部模块,维护起来更加方便和更低成本。
易扩展:面向对象有封装、继承、多态性的特性,在设计系统阶段可以设计出高内聚、低耦合的系统结构,使得系统更灵活、更容易扩展。
易复用:也是同理,因为面向对象的三大特性,使得通用代码可以更简单的复用,提高开发效率。
缺点:
性能会比面向过程低,编程复杂度较高
使用场景:
用户需求经常变化,互联网应用,游戏,企业内部应用
🐋 七、面向接口编程的作用
在讲解面向接口编程的一些特点时,让我们先来了解下面向对象编程中五大设计原则中的两个:
🍑 7.1、符合开闭、依赖倒置原则、增强拓展性
开闭原则OCP(Open-Close Principle): 对拓展开放、对修改关闭。
依赖倒置原则DIP(Dependency Inversion Principle): 抽象不应该依赖于细节、细节应该依赖于抽象
在软件开发中,因为业务的不断变化,系统拓展性是时刻存在的。以数据库为例,在没有引入接口之前,项目一开始因为业务量较小的原因,设计的时候我们会是直接通过代码来实现对某一数据库的一系列操作,抽象出来就如下图显示:
但是随着业务的增多和复杂,单个传统关系型数据库并不能满足我们的业务,需要引入非关系型数据库做中间层和引入更先进数据库做数据存储,抽象出来如下:
很明显,因为我们一开始业务简单就只考虑了一种数据库,导致操作数据库的代码强依赖了具体的实现类,如今因为业务变化需要引入了其他不同的数据库,为了达到这个目的,我们不得已要去修改原来业务的代码,这样的修改显然是违背了面向对象编程的开闭原则、依赖倒置原则和带来了潜在的风险,因此,这样的设计并不合理,故我们引入了接口,抽象出来如下:
如上图所示,有了接口这一标准,我们的JAVA程序无需直接依赖对应的实现类,每种数据库厂商实现JDBC标准,给使用者提供一个驱动,如果我们因为业务变动需要引入第三方数据库,只需要修改对应的驱动即可,不需要变动原来的逻辑,不仅满足了业务代码不再依赖实现类,而是实现类依赖接口即依赖倒置原则「抽象不应该依赖于细节,细节应该依赖于抽象」和开闭原则[对拓展开放,对修改关闭],还避免了改动带来的潜在风险。
接口中定义了规范、不同的实现者去根据自己需求实现规范,后面如果有新的需求,可以创建新的实现者实现接口规范即可,这样做到了代码的松耦合。
最常见的例子就是: JDBC(JAVA数据库连接),它是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,是Java访问数据库的标准规范。
无规则聚不成方圆,如果没有JDBC的话访问的数据库操作就没有了规范,所有的数据库厂商都会有自己的一套数据库的访问规则,一旦公司的数据库从切换数据库,整个涉及到数据库的代码都需要重新写、这对开发者来说简直是世界末日。
所以sun公司统一了数据库的规范,不同的数据库厂商需要提供实现了这个规范的一个驱动,用于开发者连接数据库时进行通信,有了JDBC,开发者不需要关注如何去编写针对不同数据库的访问代码,变动数据库时,只需要去切换不同的驱动即可。
PS:JDBC是接口,驱动是接口的实现,没有驱动将无法完成数据库连接,从而不能操作数据库! 每个数据库厂商都需要提供自己的驱动,用来连接自己公司的数据库,也就是说驱动一般都由数据库生成厂商提供。
🐇 八、抽象类和接口的关系
看到这里,相信有很多小伙伴会有疑问,能否使用抽象类替换接口,毕竟两者概念上很容易模糊。在个人看来,JAVA中,除了类只能被单继承,但是接口可以多实现(接口之间可以多继承)这个限制外,最重要的是两者设计的一个目的。
抽象类设计出来的目的是为了抽取出某一个种类的一些共有特性或者默认行为,以达到代码复用的目的。而接口则是为了规定某一种标准而设计出来,它强调的是规范,在面向对象语言中体现就是多态性的使用。 所以,如果出现该设计为抽象类还是接口纠结的场景,建议可以从设计的动机方面进行考虑,应该能得到一个比较好的结果。
🐈 九、标记接口
特点:
标记接口(Market Interface),也叫标签接口(Tag Interface),它表示的是没有任何方法和属性的接口,它仅仅表明被标记的类属于一个特定的类型,供其他代码来测试允许做一些事情。
上面的英文是维基百科关于标记接口的一个描述,由此,我们可以知道,标记接口并不是JAVA语言独有的东西,而是计算机科学中的一种设计理念,用于给面向对象语言描述对象。
因为编程语言本身并不支持为类维护元数据,而标记接口可以用作描述类的元数据,弥补了这个功能上的缺失。对于实现了标记接口的类,我们就可以在运行时通过反射机制去获取元数据。
在JDK中,我们最常接触到的标记接口有许多如: Serializable、Cloneable、RandomAccess等,如果一个类实现了Serializable接口,表示这个类是可以被序列化的,实际上的原理就是实现了这个接口的类就会被打上一个【可序列化】的标签,在运行时可以通过反射去获取然后进行一系列的操作。
在JAVA中,标记接口的作用主要在以下两方面:
1、建立一个公共的父接口:
正如EventListener接口,这是由几十个其他接口扩展的Java API,你可以使用一个标记接口来建立一组接口的父接口。例如:当一个接口继承了EventListener接口,Java虚拟机(JVM)就知道该接口将要被用于一个事件的代理方案。
2、向一个类添加数据类型
这个是标记接口最初的目的,实现标记接口的类不需要定义任何接口方法(因为标记接口根本就没有方法),但是该类通过多态性变成一个接口类型即可以使用这个接口去接收实现了它的具体类的实例。
🐧 十、关于面向接口编程在实际运用的一些问题
10.1、为什么现在很多项目从controller、service到dao层都是使用: 接口 + 实现类的方式、但是只有一个实现类,这种方式是否有必要?
我敢说,这个问题的争论一定不会少,大家都认为自己说得有理,很难去让一个使用接口+ 实现类的人去说服一个直接使用实现类的人,反之亦然,在此处,收集了许多看法,欢迎大家讨论。
一、支持:接口 + 实现类的模式、理由如下
看法1: 如果使用Spring框架作为项目开发,推荐使用这种方式,这样才能最大化的发挥Spring IOC的用途,更方便使用和减少内存使用。
看法2: 利于维护和拓展,你现在看到的是只有一个实现类,但是随着业务的复杂变化,后面就可能出现多个实现类,使用该种方式如果有新的业务变动,直接添加新的实现类即可,无需改动原来的代码。
看法3: 利于团队合作,不同的人可能负责不同模块的开发如Controller、Service、Dao,只要定义好接口,负责各个模块的人即可去负责对应的开发。
看法4: 控制暴露给外部的内容,保证安全性的问题。接口只会暴露定义公开的部分,但是直接使用对象则可以看到私有部分,这些私有部分可能会有安全性问题。
看法5: 这个属于编程规范要求,封装实现细节,暴露对外接口,这也是“高内聚,低耦合”的思想。
看法6: 符合开闭原则,降低变动带来的风险,便于理解,因为很多源码也是使用这种方式。
看法7: 利用多态性,同一个接口可以存在多个实现类,可以配合反射和配置文件的使用,实现代码的解耦,需要增强功能时,只需要添加新的类和修改配置文件即可达到目的。
二、不支持:接口 + 实现类的模式,支持直接使用实现类方式,理由如下
看法1: 经过大多数实际项目经验,百分之99接口和实现类都是1:1的实现,在这种情况下,多定义一个接口就没有必要,而且还需要维护额外的代码,所以没必要为了规范而规范,如果是小团队,公司技术水平比较平均,直接使用实现类即可,没必要定义接口。
🐉 十一、写在最后
好了,关于面向接口编程的介绍今天介绍到这里,大家有不同看法的欢迎在下方留言,欢迎大家向我投稿面试中遇到的奇葩问题噢,学习了就要运行,赶紧拿着文章找个面试官对线一波吧。如果文章对你有帮助,不要忘记三连呀!
顺便预告下篇文章的内容,不知不觉写博客已经两年了,粉丝虽然不多,但是也准备到了6千,所以准备给大家反馈下福利(主要是关于写博客的素材和面试真题的),感兴趣的可以留意下,最近因为工作原因和自我质量要求原因,博客的更新频率会大概保持在每周一更左右,希望能够给大家带来更有质量的文章!
🐬 十二、参考资料