Service 层和 Dao 的接口是不是多此一举?

简介: 今天我们要探讨的问题是:Service 层和 Dao 的接口是不是多此一举?现在结合我参与的项目以及阅读的一些项目源码来看。如果「项目中使用了像Spring这样的依赖注入框架,那可以不用接口」!先来说说为什么使用了依赖注入框架以后,可以不使用接口!

不需要接口的理由


我整理了支持Service层和Dao层需要加上接口的理由,总结下来就这么三个:


可以在尚未实现具体Service逻辑的情况下编写上层代码,如Controller对Service的调用


Spring默认是基于动态代理实现AOP的,动态代理需要接口


可以对Service进行多实现



实际上,这三个理由都站不住脚!


先说说第一个理由:「上层可以在下层逻辑没有实现的情况下进行编码」!很典型的面向接口编程,对层与层之间进行了解耦,看起来好像没有问题。


这种开发方式适合不同模块之间是由不同的人或项目组开发的,因为沟通的成本比较大。同时避免由于项目组之间开发进度的差异而相互影响。


不过让我们回想一下,在一般项目开发里面,有多少项目组是按层来切分开发任务的呢?实际上,大部分的项目都是按照功能划分的。即使是现在前后端分离的情况,单纯的后端开发也是按照功能模块进行任务划分,即一个人负责从Controller层到DAO层的完整逻辑处理。在这种情况下,每一层都先定义一个接口,再去实现逻辑,除了增加了开发人员的工作量(当然,如果代码量计入工作量的话,那开发人员应该也不是太排斥接口的!),实际没有任何用处。


如果开发人员想在下层逻辑没有完成的情况下,先开发上层逻辑,可以先编写下层类的空方法来先完成上层的逻辑。


这里推荐一个个人比较喜欢的开发流程,自上向下的编码流程:


先在Controller层编写逻辑,遇到需要委托Service调用的地方,直接先写出调用代码。优先完成Controller层的流程


然后使用IDE的自动补全,对刚才调用下层的代码生成对应的类和方法,在里面添加TODO


等所有的类和方法都补全了,再基于TODO,按照上面的流程去一个个的完善逻辑。


此方法可以使你对业务流程有比较好的理解


对于第二个理由,就完全不成立了。Spring默认是基于动态代理的,不过通过配置是可以使用CGLib来实现AOP。CGLib是不需要接口的。


最后一个理由是「可以对Service进行多实现」。这个理由不充分,或者说没有考虑场景。实际上在大多数情况下是不需要多实现,或者说可以使用其它方式替代基于接口的多实现。


另外,对于很多使用了接口的项目,项目结构也是有待商榷的!下面,我们结合项目结构来说明。


项目结构与接口实现


一般项目结构都是按层来划分的,如下所示:


Controller


Service


Dao


对于不需要多实现的情况,也就不需要接口了。上面的项目结构即可满足要求。


对于需要多实现的情况,无论是现在需要,还是后面需要。这种情况下,看起来好像是需要接口。此时的项目结构看起来像这样:


Controller

Service


----接口在一个包中

impl —实现在另一个包里

Dao


对于上面的结构,我们来考虑多实现的情况下,该怎么处理?


第一种方式,是在Service中新增一个包,在里面编写新的逻辑,然后修改配置文件,将新实现作为注入对象。


Controller

Service


---- 接口在一个包中

impl —实现在另一个包里

impl2 —新实现在另一个包里

Dao


第二种方式,是新增一个Service模块,在里面编写新的逻辑(注意这里的包和原来Service的包不能相同,或者包相同,但是类名不同,否则无法创建类。因为在加载时需要同时加载两个Service模块,如果包名和类名都相同,两个模块的类全限定名就是一样的了!),然后修改配置文件,将新逻辑作为注入对象。


Controller

Service


---- 接口在一个包中

impl —实现在另一个包里

Service2


impl2 —新实现在另一个包里

Dao


相对而言,实际第一种方式相对更简单一点,只需要关注包层面。而第二种方式需要关注模块和包两个层面。另外,实际这两种方式都导致了项目中包含了不需要的逻辑代码。因为老逻辑都会被打进包里。


不过,从结构上来看,实际方式二的结构要比方式一的结构更清晰,因为从模块上能区分逻辑。


那有没有办法来结合两者的优点呢?答案是肯定的,而且操作起来也不复杂!


首先将接口和实现独立开,作为一个独立的模块:


Controller

Service — 接口模块

ServiceImpl


impl —实现在另一个包里

ServiceImpl2


impl2 —新实现在另一个包里

Dao


其次,调整打包配置,ServiceImpl和ServiceImpl2二选一。既然ServiceImpl和ServiceImpl2是二选一,那ServiceImpl和ServiceImpl2的包结构就可以相同。包结构相同了,那调整了依赖以后,依赖注入相关的配置就不需要调整了。调整后,项目结构看起来像这样:


Controller

Service — 接口模块

ServiceImpl


impl —实现在另一个包

ServiceImpl2


impl —新实现和老实现在相同的包中

Dao


现在,ServiceImpl和ServiceImpl2模块中的包结构、类名都是一样的。那我们还需要接口模块吗?


假设,我们把Service接口模块去掉,结构变成了如下所示:


Controller

Service1 — 老实现

Service2 — 新实现

Dao


单纯的通过调整模块依赖,是否能实现Service的多实现?答案显而易见吧?


不使用接口的缺点


上面给出了不使用接口的理由。不过不使用接口并不是完全没有缺点的,主要问题就是在进行多实现的时候,没有一个强接口规范。即不能通过实现接口,借助IDE快速生成框架代码。对于没有实现的接口,IDE也能给出错误提醒。


一个不太优雅的解决是,将原来的模块里的代码拷贝一份到新模块中,基于老代码来实现新的逻辑。


所以,如果一个项目需要多实现、且多实现数量较多(不过一般项目不会有多个实现的),则推荐使用接口。否则不需要使用接口。


总结


本文针对「Service层是否需要接口」这个问题,指出需要接口的理由的问题。以及个人对这个问题的观点,希望对大家有一些帮助。

目录
相关文章
|
网络协议 Java Nacos
Nacos报错问题之jar 包启动就报错误如何解决
Nacos是一个开源的、易于部署的动态服务发现、配置管理和服务管理平台,旨在帮助微服务架构下的应用进行快速配置更新和服务治理;在实际运用中,用户可能会遇到各种报错,本合集将常见的Nacos报错问题进行归纳和解答,以便使用者能够快速定位和解决这些问题。
1052 2
|
Java 关系型数据库 Linux
Linux|Java|jar包的解压和重新打包(更新配置)
Linux|Java|jar包的解压和重新打包(更新配置)
800 0
|
2月前
|
数据安全/隐私保护 iOS开发
Mac mini 带回老家,打算用远程控制,第一次开机我傻眼了
Mac mini回老家远程办公?别急!首次开机需本地输密码+连Wi-Fi,无屏幕/键盘/Type-C转接头极易卡死。iPad最省心,电视临时救急,旧笔记本远程前必先“本地破冰”。提前备齐外设,否则真会傻眼!
224 3
|
9月前
|
人工智能 自然语言处理 数据可视化
企业AI落地开源五剑客:Open-WebUI、Dify、RAGFlow、FastGPT、n8n
面对企业AI落地的数据安全、技术门槛和业务整合三大痛点,本文推荐五款开源利器:Open-WebUI(零代码交互)、Dify(低代码工厂)、RAGFlow(知识处理)、FastGPT(内容生成)和n8n(流程自动化)。这些工具提供开源可控、私有化部署和模块化扩展能力,助力企业低成本构建完整AI解决方案,突破传统闭源方案的成本与灵活性限制。
|
自然语言处理 数据可视化 Python
利用Python爬取百度百科词条并生成词云图
本文介绍如何使用Python爬取百度百科词条内容并生成词云图,涉及`requests`、`BeautifulSoup`、`jieba`、`wordcloud`等库的使用,从环境准备、数据爬取、中文分词到词云图生成,详细展示了整个流程。
647 0
|
网络协议 网络安全 网络架构
移动宽带不借助软件和公网服务器实现基于IPV6的内网穿透
本教程指导如何设置路由器以支持IPv6访问:首先确保上网方式为自动获取IP,接着在路由器设置中开启IPv6功能,并关闭可能阻碍连接的防火墙。最后,在光猫管理界面同样关闭防火墙以确保无障碍的IPv6访问路径。操作时请注意网络安全。
移动宽带不借助软件和公网服务器实现基于IPV6的内网穿透
|
存储 运维 监控
自动化运维:使用Python脚本进行服务器监控
【8月更文挑战第31天】在数字化时代,服务器的稳定运行对于企业至关重要。本文将介绍如何使用Python编写一个简单的服务器监控脚本,帮助运维人员及时发现并解决潜在问题。我们将从基础的服务器资源监控开始,逐步深入到日志分析与报警机制的实现。通过实际代码示例和操作步骤,使读者能够快速掌握自动化监控的技能,提升工作效率。
|
Java 定位技术 API
Docker: java.lang.NoClassDefFoundError: sun.awt.X11FontManager
Docker: java.lang.NoClassDefFoundError: sun.awt.X11FontManager
Docker: java.lang.NoClassDefFoundError: sun.awt.X11FontManager
|
前端开发 JavaScript 应用服务中间件
个人博客网站如何实现https重定向(301)到http
对于个人网站站注册比较少的,服务器配置不是很好的,没必要https,https跳转到http是要时间的,会影响网站打开的速度。免费的https每年都要更换。
784 2