当前位置: 首页 > 科技观察

CTO问:Service层真的需要接口吗?

时间:2023-03-11 20:51:18 科技观察

前几天碰到一个问题:真的有必要在Service层和Dao层的每个类都加一个接口吗?这个问题之前简单回答过,给出的观点视情况而定!今天我们要讨论的问题是:Service层需要接口吗?图片来自Pexels,现结合我参与的项目和阅读的一些项目的源码。如果你在项目中使用像Spring这样的依赖注入框架,你就不需要使用接口了!先说说为什么用了依赖注入框架就不用接口了!整理了不需要接口支持Service层和Dao层的原因。加接口的原因可以归结为三种:可以写上层代码,不用实现具体的Service逻辑,比如Controller对Service的调用。默认情况下,Spring基于动态代理实现AOP,动态代理需要接口。服务的多种实现是可能的。其实这三个理由都是站不住脚的!先说第一个原因:上层可以不实现下层的逻辑就可以编码!很典型的面向接口编程,理解层与层之间的耦合,好像没什么问题。这种开发方式适合不同的模块由不同的人或者项目组来开发,因为沟通成本比较高。同时,避免项目团队之间因开发进度的差异而相互影响。但是大家回想一下,在一般的项目开发中,有多少项目组会层层划分开发任务呢?事实上,大多数项目都是按功能划分的。即使在目前前后端分离的情况下,简单的后端开发也是按照功能模块分工,即一个人负责从Controller层到DAO层的完整逻辑处理.这样的话,每一层都是先定义一个接口,然后再实现逻辑,除了增加开发人员的工作量之外(当然,如果把代码量算在工作量中,那么开发人员也不要太排斥了接口),这实际上是无用的。如果开发者想先开发上层逻辑而不完成下层逻辑,可以先写下层类的empty方法先完成上层逻辑。这里推荐一个我个人比较喜欢的开发流程,自上而下的编码流程:先在Controller层写逻辑,遇到需要委托Service调用的地方直接写调用代码。优先完成Controller层的流程。然后利用IDE的自动补全功能,为刚才调用下层的代码生成相应的类和方法,并在其中添加TODO。所有的类和方法完成后,基于TODO,按照上面的流程,将逻辑一一完善。这种方法可以让您更好地了解业务流程。第二个理由,完全站不住脚。Spring默认是基于动态代理的,但是可以通过配置使用CGLib来实现AOP。CGLib不需要接口。最后一个原因是一个服务可以有多个实现。这个理由不充分,还是没有考虑到现场。事实上,在大多数情况下,不需要多重实现,或者可以使用其他方法代替基于接口的多重实现。另外,对于很多使用接口的项目,项目结构也是有待商榷的!下面,我们将以项目结构为例进行说明。项目结构和接口实现一般情况下,项目结构是按层次划分的,如下图:ControllerServiceDao如果不需要更多的实现,就不需要接口。以上项目结构可以满足要求。对于需要更多实施的情况,无论是现在还是以后。在这种情况下,看起来需要一个接口。此时的项目结构是这样的:ControllerService---一个包中的接口impl---另一个包中的实现Dao对于上面的结构,我们来考虑多个实现的情况,如何处理?第一种方式是给Service添加一个新的包,在里面写新的逻辑,然后修改配置文件,使用新的实现作为注入对象。如下图:ControllerService---一个包中的接口impl---另一个包中的实现impl2---另一个包中的新实现Dao第二种方式是添加一个Service模块,在里面写新的逻辑(注意这里的包不能和原来的Service包一样,或者包是一样的,但是类名不同,否则不能创建类。因为加载的时候需要同时加载两个Service模块,如果包名和类名都相同,两个模块的完全限定类名相同),然后修改配置文件以使用新的逻辑作为注入对象。如下图:ControllerService---一个包中的接口实现---另一个包中的实现Service2impl2---另一个包中的新实现Dao相对来说,实际第一种方式比较简单,只需要注意包级别。第二种方法需要关注模块和包两个层次。另外,这两种方式实际上都会导致项目中包含不必要的逻辑代码。因为旧的逻辑会被打包进包里。但是从结构上看,实际方法2的结构比方法1更清晰,因为可以从模块中区分出逻辑。有没有办法结合两者的优点?答案是肯定的,而且操作也不复杂!首先,将接口和实现分离为一个独立的模块:ControllerService---接口模块ServiceImplimpl---实现在另一个包中ServiceImpl2impl2---新的实现在另一个包Dao中其次,调整打包配置,选择ServiceImpl中的一个和ServiceImpl2。由于ServiceImpl和ServiceImpl2是可选的,所以ServiceImpl和ServiceImpl2的包结构可以相同。包结构是一样的,所以调整完依赖后,依赖注入相关的配置就不用再调整了。调整后工程结构如下:ControllerService---接口模块ServiceImplimpl---另一个包中的实现ServiceImpl2impl---同一个包Dao中的新实现和旧实现现在,ServiceImpl和ServiceImpl2模块中的包结构,Class名字是一样的。我们还需要接口模块吗?假设我们去掉Service接口模块,结构变成如下:ControllerService1---Service2的旧实现---Dao的新实现仅仅通过调整模块依赖,是否可以实现ServiceMultipleimplementations?答案很明显,对吧?不使用接口的缺点上面给出了不使用接口的原因。但是,不使用接口并非完全没有缺点。主要问题是在实现多个实现时没有强接口规范。即无法通过实现接口,借助IDE快速生成框架代码。对于没有实现的接口,IDE也可以给出错误提示。一个不优雅的解决方案是将代码从原始模块复制到新模块,并在旧代码的基础上实现新逻辑。因此,如果一个项目需要多次实现,并且多次实现数量较多(但一般项目不会有多次实现),建议使用接口。否则没有必要使用接口。总结本文针对Service层是否需要接口的问题,指出了需要接口的原因。以及我个人对这个问题的看法,希望对大家有所帮助。作者:建筑思维编辑:陶家龙来源:toutiao.com/i6882356844245975563