面向接口编程到底有什么意义?

发布时间 2024-01-06 10:28:51作者: Narcissu5

或许很多Java程序员和我一样,审视自己做过的项目,会有这样的疑问:

定义了很多接口但是都只有一种实现,这样的接口到底有没有意义?

首先面向接口编程肯定是有意义的,Java基本库本身就是一本很好的教科书,难以想象没有接口的Java基本库会是什么样子。写代码不使用接口也不是不行,编程不止一种范式,很多语言就没有接口这种东西,但至少在Java的世界里,接口确实是Java哲学的核心部分之一。

那么,是我们的打开方式有什么问题么?

首先,接口别太大

接口越大,其职责越复杂,实现起来也越是困难,复用性也就越差。接口方法越少,职责越单一,也就越容易有多种不同的实现,也就是所谓的单一职责原则。

一个很好的反面例子就是.Net的MembershipProvider,这是.Net Web方面的一个核心类,洋洋洒洒几十个方法,提供用户登录权限方面的功能。这个类是.net长期以来的一个痛点,那么痛在哪里呢?

这个类诞生的时间非常早,当时网站还是以邮箱+密码登录为主。这个类的设计完全基于这种场景,然而时过境迁,如今登录方式多种多样,由于这个类太大,新的登录方式很
难通过继承这个类的方式实现接入。例如:你想增加微信扫码登录,可是ResetPassword这个方法又该如何实现呢?当然你也可以选择不支持该方法,可是当大部分方法都不支持的时候,它还能正常工作么?实际上微软自己也在这个类上花了很多时间去做兼容,都是白费时间,最后也不得不另起炉灶。

当然,这是一个虚类而非接口,不过它本应是一个接口的,这又是一个微软的渣设计

再看看Spring Security的设计,仅有两个方法,瞬间给人一种“我也能实现一个”的感觉:

public interface AuthenticationProvider {  

	Authentication authenticate(Authentication authentication) throws AuthenticationException;  
  
	boolean supports(Class<?> authentication);  
}

当然,两者的诞生时间、设计背景都不一样,直接比较也不太公平。但如果.net web的初版设计能稍微想得长远一些,设计得稍微通用一些,可能整个.net的生态都会少走很多弯路。

其次,花点时间来抽象吧

接口既是抽象的载体,也是抽象的产物,可以说没有抽象就没有接口。而抽象必须建立在有意识的主动的思考上,在一头扎进编码之前,花点点时间思考一下是很有意义的。我们需要想清楚:哪些概念是抽象的通用的,这归于接口;哪些是具体的实现相关的,这归于实现类。接口因此才能诞生。

但很多时候我们会觉得这些思考与设计并没有什么用处:一旦真正进入开发阶段,所有的设计都会失效,事情的发展总是和之前设想的不一样。代码野蛮生长,很难沿着设计走下去,之前的设计反而成为一种限制。其实这也是正常的,毕竟写代码也算是开发工作,所谓开发必然存在着未知,必然会遇到未曾设想的问题。也不必一定拘泥于事前的设计,不妨试试:写完代码之后再试着提取接口

我们总是在工作完成之后才对这些工作有着更深刻的认识,这时候来设计接口反而更加合适。现代IDE很强大了,提取个接口出来也就是几下点击的事情,整个过程高效而又安全,再配合足够的单元测试覆盖,基本可以无痛完成。而且此时提取的接口往往接地气得多。

最后,或许可以试试设计模式

面向对象是个好东西,但只有和设计模式相结合之后才有了灵魂。如果只是用于抽象行为,似乎接口也没有那么大的用处,也就没有那么大的吸引力。但如果把接口放在设计模式的世界里,事情可就没那么简单了。

设计模式的学习和使用还是有一点门槛的,最困难的是如何落实到实际工作中。在我看来也没必要把所有模式都背下来,时常复习一下,回望一下最近写的代码是否适用,如果可以就重构成设计模式的方式,循序渐进,慢慢上手慢慢享受到设计模式的好处,之后自然就会顺利得多了。