图片 来源:https://urlify.cn/Vjua2e

👉 欢迎加入小哈的星球 ,你将获得: 专属的项目实战 / Java 学习路线 / 一对一提问 / 学习打卡 /  赠书福利

新项目:仿小红书(微服务架构)正在更新中… , 全栈前后端分离博客项目 2.0 版本完结啦, 演示链接http://116.62.199.48/">http://116.62.199.48/ 。全程手摸手,后端 + 前端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,直到项目上线。目前已更新了255小节,累计40w+字,讲解图:1716张,还在持续爆肝中.. 后续还会上新更多项目,目标是将Java领域典型的项目都整一波,如秒杀系统, 在线商城, IM即时通讯,Spring Cloud Alibaba 等等,戳我加入学习,已有1300+小伙伴加入(早鸟价超低)

图片

前几天看技术交流群的话题,又刷到了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 学习路线 / 一对一提问 / 学习打卡 /  赠书福利

新项目:仿小红书(微服务架构)正在更新中… , 全栈前后端分离博客项目 2.0 版本完结啦, 演示链接http://116.62.199.48/">http://116.62.199.48/ 。全程手摸手,后端 + 前端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,直到项目上线。目前已更新了255小节,累计40w+字,讲解图:1716张,还在持续爆肝中.. 后续还会上新更多项目,目标是将Java领域典型的项目都整一波,如秒杀系统, 在线商城, IM即时通讯,Spring Cloud Alibaba 等等,戳我加入学习,已有1300+小伙伴加入(早鸟价超低)

图片

<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com"><img data-fileid="100015743" data-imgfileid="100051314" data-ratio="0.08658008658008658" data-src="https://mmbiz.qpic.cn/mmbiz_gif/TNUwKhV0JpTGQqtlGfEHkjibtshlaDwVKzjqq2pnpmYC14bKxDtSuhpWZWfVcicj5PFsoSMzuzicKIWZbsBpGXiaicg/640?wx_fmt=gif&amp;wxfrom=5&amp;wx_lazy=1&amp;tp=webp" data-type="gif" data-w="462" data-width="100%" data-original-style="outline: 0px;background-size: 16px;border-radius: 8px;font-family: -apple-system, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;letter-spacing: 0.544px;word-spacing: 2px;display: block;visibility: visible !important;width: 577.977px !important;" data-index="6" src="https://mmbiz.qpic.cn/mmbiz_gif/TNUwKhV0JpTGQqtlGfEHkjibtshlaDwVKzjqq2pnpmYC14bKxDtSuhpWZWfVcicj5PFsoSMzuzicKIWZbsBpGXiaicg/640?wx_fmt=gif&amp;wxfrom=5&amp;wx_lazy=1&amp;tp=webp" _width="577.977px" data-order="0" alt="图片"></section><pre><pre><pre data-style="letter-spacing: 0.544px; font-size: 16px; color: rgb(63, 63, 63); word-spacing: 1px; line-height: inherit;"><section powered-by="xiumi.us" data-darkmode-bgcolor="rgb(36, 36, 36)" data-darkmode-original-bgcolor="rgb(255, 255, 255)" data-darkmode-color="rgb(230, 230, 230)" data-darkmode-original-color="rgb(0, 0, 0)" data-style="margin-right: 0.5em; margin-left: 0.5em; white-space: normal; font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif; color: rgb(0, 0, 0); letter-spacing: 0px; word-spacing: 2px;" data-mpa-template-id="1250" data-mpa-color="#ffffff" data-mpa-category="divider"><p data-darkmode-bgcolor="rgb(36, 36, 36)" data-darkmode-original-bgcolor="rgb(255, 255, 255)" data-darkmode-color="rgb(106, 104, 111)" data-darkmode-original-color="rgb(106, 104, 111)">1.&nbsp;<a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU4MDUyMDQyNQ==&amp;mid=2247522465&amp;idx=1&amp;sn=49da64cae0a62b0c2beac8e0ad2b420e&amp;chksm=fd574827ca20c1314c036377d7698e1fe12a7e716a15a0cfb56d5870f209c3dded22dd848d26&amp;scene=21#wechat_redirect" textvalue="我的私密学习小圈子~" linktype="text" imgurl="" imgdata="null" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1">我的私密学习小圈子~</a></p><p data-darkmode-bgcolor="rgb(36, 36, 36)" data-darkmode-original-bgcolor="rgb(255, 255, 255)" data-darkmode-color="rgb(138, 138, 138)" data-darkmode-original-color="rgb(89, 89, 89)"><span>2.&nbsp;<a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU4MDUyMDQyNQ==&amp;mid=2247534922&amp;idx=1&amp;sn=6bfb7ca2b8997f1887ec2a7d1890622e&amp;chksm=fd5799ccca2010da51a080f85274b0a349716a002dcadf0da247df280a537732d8b95c0d6c0a&amp;scene=21#wechat_redirect" textvalue="分布式链路追踪:TraceIdFilter + MDC + Skywalking" linktype="text" imgurl="" imgdata="null" data-itemshowtype="0" tab="innerlink" data-linktype="2">分布式链路追踪:TraceIdFilter + MDC + Skywalking</a><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU4MDUyMDQyNQ==&amp;mid=2247530313&amp;idx=1&amp;sn=5adf4981de7f0abbcb1bde27ee74ae56&amp;chksm=fd57a7cfca202ed9351c8954bce05f159584f0219de7bd3ff12362e3c98b1cc752802bbe3db2&amp;scene=21#wechat_redirect" textvalue="堪称最优秀的Docker可视化管理工具——Portainer你真的会用吗?" linktype="text" imgurl="" imgdata="null" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1"></a></span></p><p data-darkmode-bgcolor="rgb(36, 36, 36)" data-darkmode-original-bgcolor="rgb(255, 255, 255)" data-darkmode-color="rgb(138, 138, 138)" data-darkmode-original-color="rgb(89, 89, 89)"><span>3.&nbsp;<a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU4MDUyMDQyNQ==&amp;mid=2247534922&amp;idx=2&amp;sn=4ffb5b1be2a19ee74c8358e2f9e105e3&amp;chksm=fd5799ccca2010daa8fac1c60811af84ae8e2b6eca60ea2a49d68c154078e104f26df7e54924&amp;scene=21#wechat_redirect" textvalue="微服务架构中10个常用的设计模式" linktype="text" imgurl="" imgdata="null" data-itemshowtype="0" tab="innerlink" data-linktype="2">微服务架构中10个常用的设计模式</a><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU4MDUyMDQyNQ==&amp;mid=2247531213&amp;idx=2&amp;sn=f4d4ae2ffc511109f0917b8e15217e65&amp;chksm=fd57aa4bca20235d13f288853171b10e40ebd9ce272880c36de9a40a2d136d32a665ac4ca2a6&amp;scene=21#wechat_redirect" textvalue="常见踩坑点:双层 for 循环千万别这样写了!" linktype="text" imgurl="" imgdata="null" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1"></a></span></p><p data-darkmode-bgcolor="rgb(36, 36, 36)" data-darkmode-original-bgcolor="rgb(255, 255, 255)" data-darkmode-color="rgb(106, 104, 111)" data-darkmode-original-color="rgb(106, 104, 111)">4.&nbsp;<a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU4MDUyMDQyNQ==&amp;mid=2247534774&amp;idx=2&amp;sn=09125d0e2d8f106e1cad7cfdbe3207a2&amp;chksm=fd579830ca20112641818283bdf9e4d3fc08078494d853e4b88531cf59dad83f1144f66003d6&amp;scene=21#wechat_redirect" textvalue="Hutool 中的这些工具类,太实用了!" linktype="text" imgurl="" imgdata="null" data-itemshowtype="11" tab="innerlink" data-linktype="2">Hutool 中的这些工具类,太实用了!</a><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU4MDUyMDQyNQ==&amp;mid=2247531127&amp;idx=1&amp;sn=82474072ee5c6ca729f17ec75ded9e72&amp;chksm=fd57aaf1ca2023e7c9960deabf609a3b75a2da35fa0717aea6ef958025948da270dc08930007&amp;scene=21#wechat_redirect" textvalue="SpringBoot:一个注解就能帮你下载任意对象" linktype="text" imgurl="" imgdata="null" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1"></a></p></section><p data-darkmode-bgcolor="rgb(36, 36, 36)" data-darkmode-original-bgcolor="rgb(255, 255, 255)" data-darkmode-color="rgb(168, 168, 168)" data-darkmode-original-color="rgb(62, 62, 62)" data-style="margin-right: 0.5em; margin-left: 0.5em; white-space: normal; font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif; letter-spacing: 0px; word-spacing: 2px; color: rgb(62, 62, 62); text-align: center;"><img data-fileid="100015744" data-imgfileid="100051315" data-ratio="0.5555555555555556" data-s="300,640" data-type="gif" data-w="639" data-src="https://mmbiz.qpic.cn/sz_mmbiz_gif/knmrNHnmCLEVGGmicJODkfibhcqyUwmTSC8CUvAMG78wPemfibvQ502uFs9jlziaLP50YcTs4rL9hQuzX32PAUOPHA/640?wx_fmt=gif&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1&amp;tp=webp" data-original-style="outline: 0px;border-radius: 8px;background-position: center center;background-repeat: no-repeat;background-color: rgb(238, 237, 235);background-size: 22px;border-width: 1px;border-style: solid;border-color: rgb(238, 237, 235);visibility: visible !important;width: 601px !important;" data-index="7" src="https://mmbiz.qpic.cn/sz_mmbiz_gif/knmrNHnmCLEVGGmicJODkfibhcqyUwmTSC8CUvAMG78wPemfibvQ502uFs9jlziaLP50YcTs4rL9hQuzX32PAUOPHA/640?wx_fmt=gif&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1&amp;tp=webp" _width="601px" crossorigin="anonymous" alt="图片"></p><pre data-style="letter-spacing: 0.544px; text-size-adjust: auto; word-spacing: 2px; color: rgb(89, 89, 89);"></pre></pre></pre></pre>
<pre data-style="letter-spacing: 0.544px; text-size-adjust: auto; word-spacing: 2px; color: rgb(89, 89, 89);"><p data-style="margin-top: 5px; margin-bottom: 5px; white-space: normal; color: rgb(62, 62, 62); letter-spacing: 0.544px; font-family: &quot;Helvetica Neue&quot;, Helvetica, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif; font-size: 14px; line-height: normal;"><span>最近面试BAT,整理一份面试资料</span><span>《<strong>Java面试BATJ通关手册</strong>》</span><span>,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。</span></p><p data-style="margin-top: 15px; margin-bottom: 15px; letter-spacing: 0.544px; white-space: pre-line; line-height: 30px; color: rgb(74, 74, 74); font-family: Avenir, -apple-system-font, 微软雅黑, sans-serif;"><span>获取方式:点“</span><span>在看</span><span>”,关注公众号并回复&nbsp;</span><span>Java</span><span>&nbsp;领取,更多内容陆续奉上。</span></p></pre><pre><p data-tool="mdnice编辑器"><span>PS:因公众号平台更改了推送规则,如果不想错过内容,记得读完点一下</span><strong>“<span>在看</span>”</strong><span>,加个</span><strong>“<span>星标</span>”</strong><span>,这样每次新文章推送才会第一时间出现在你的订阅列表里。</span></p><p data-tool="mdnice编辑器"><span>点<strong>“在看”</strong>支持小哈呀,谢谢啦</span></p></pre>