性能文章>真理大讨论:Service层的接口是不是多此一举?>

真理大讨论:Service层的接口是不是多此一举?原创

1年前
345388
  • 分歧出现的背景

  • 这是谁的问题?代码架构的问题?

  • 有什么问题?

  • 解决问题的思路是什么?

  • 总结

背景

这个问题要从业界鼎鼎大名的三层架构说起: 表示层、业务逻辑层、数据访问层。
似乎从一开始接触写代码,好像就有一个不成文的规定,Service 层往往要写一个 接口,然后再写一个实现类
如下图所示:



CRUD写的久了,确实会有一个疑惑,这层接口到底有没必要?(我们只讨论普通的、常见的场景)从工作的这么多年来看,这一层好像没太大作用,大多数接口一般只有一个实现类,大多数Service层中实现Interface的method中完成了所有的业务逻辑。
在网上搜了下,各说各的道理,各有各的大佬站台。这个点在团队内部也会出现分歧,并且引发内耗,所以也研究了一下。
毕竟网上的大佬从开发流程和功能实现的角度已经剖析的比较清楚了,那我们就换个视角来讨论下这个老问题,看看有没有些许帮助

1、这是谁的问题?

是一个代码架构的问题。因为是否抽接口,其实是在讲代码的结构。

关于架构,我们再展开聊几句。

什么是架构?一个顶层结构。是为了解决复杂度带来的问题。 

什么是代码架构? 软件系统中的组织结构和设计原则,
旨在实现系统的 可靠性可扩展性可维护性易于理解等特性。
代码架构涉及到对系统的整体结构进行规划和设计,包括模块划分、组件之间的关系、数据流向以及代码的组织方式等方面。
ChatGPT3.5

什么是好的架构?

坏的架构都是相似的,但好的架构通常具备以下几个重要的特点:

模块化:好的架构将系统划分为独立的模块或组件,每个模块都有清晰的职责和功能。这样的模块化设计使得系统更易于理解、维护和扩展。
松耦合:好的架构通过降低模块之间的依赖程度来实现松耦合。模块之间的交互应该通过明确定义的接口进行,从而减少对其他模块的依赖。这种松耦合使得系统中的更改可以局部化,不会对整个系统产生过大的影响。
高内聚:好的架构追求高内聚性,即相关功能和数据应该被组织在一起。这样可以使得模块内部的逻辑清晰,并且模块之间的通信和协作更加紧密和高效。
可扩展性:好的架构应该能够容易地进行功能扩展或者适应未来的需求变化。通过合理的模块划分和设计原则,新的功能可以被添加进系统而不需要修改现有的代码。
可重用性:好的架构鼓励代码的重用。通用的功能和组件应该被抽象和封装起来,使得它们可以在系统的不同部分或者其他项目中被重复利用。这样可以提高开发效率并减少代码冗余。
易于理解:好的架构应该具备良好的可读性和可理解性。清晰的结构、命名规范和文档化有助于开发人员快速了解系统的组织和设计原则。
性能和可靠性:好的架构需要考虑系统的性能和可靠性。通过优化数据流、减少资源消耗以及使用合适的设计模式,可以提高系统的性能,并减少潜在的错误和故障。
可测试性:好的架构支持易于编写和执行测试的设计。通过松耦合和模块化,可以更轻松地对每个模块进行单元测试,并确保系统的正确性和稳定性。

好的架构让你可以延迟做出一些重要的决定,可以在面对不确定性和变化时保持灵活性、可扩展性和可维护性。

2、有什么问题?

业务逻辑层中的每个类都抽一个接口,在大多数时候好像没有用,投入产出比不高。

为什么会“没什么用”?
大多数程序员的日常是这样的:领了需求,写一个Controller、一个Service、一个DAO就完事了。
期间,不用写单元测试,顶多写功能测试。

总结一下:
我们CRUD“程序猿”不需要。

事实上大多数系统业务都比较简单,生命周期也短,一些创业项目3个月看不到收益就下掉了,因此不需要扩展,因为够简单;
不需要易于理解,因为够简单,代码够少。使用设计模式还可能被置疑有过度设计之嫌。
不需要可测试性,因为够简单,一眼逻辑就看到底了,调下接口看下就Ok了,最多写个功能测试

因此,就很容易得到这个结论:
如果一个项目需要多实现、且多实现数量较多(不过一般项目不会有多个实现的),则推荐使用接口。否则不需要使用接口。

先抛个问题:在系统没有game over前,那个大佬敢站出来讲:

这个项目很简单,不需要多实现、实现类也不会有多个?

3、解决的思路是什么?

如何让代码架构变得更好!
“好的架构让你可以延迟做出一些重要的决定,可以在面对不确定性和变化时保持灵活性、可扩展性和可维护性。”
面向抽象编程,让代码的可扩展性更好,让代码可以更容易适应需求的变化。
在Service的业务类都抽一个接口,会增加工作量,根据经验,从长期来看ROI是正向的。具体原因后面会讲到。
另外,现在的IDE的自动补全功能可以很容易生成一个类、一个方法。
抽接口 其它方面的收益:

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

Spring 默认是基于动态代理实现 AOP 的;

动态代理需要接口可以对 Service 进行多实现

架构思维,公众号:51CTO技术栈CTO问:Service层真的需要接口吗?
3.1 层与层之间使用接口来实现信息隐藏。并不是所有的public方法都可以被上层消费。
在java中,会使用public、protected和private这些访问修饰符,可以控制类的封装性、实现信息隐藏,并确保良好的代码组织和可维护性。

3.2 在一个小的开发团队中,继续落地这个约定俗成的规范,可以降低内耗,提升研发效率,不然一个小项目的Service层可能会有N种写法。
当然,统一不抽接口,也有这个效果。
3.3 轻松应对需要 支持回滚的业务场景
增加一个新的实现类写新需求,要比在已经上线的方法中增加if else更加可靠、优雅。

总结
上层依赖下层的接口更稳定、更易于理解,在扩展性、维护性、可测试性也更好。

建立合适的代码规范也可以降低团队开发之间因代码风格不同引发的内耗,提升研发效率。

代码规范示例:

参考
https://www.oschina.net/question/2652412_2323397
#小程序://知乎/cTkkAK4oaPRdjlf

 

点赞收藏
bit01
请先登录,查看8条精彩评论吧
快去登录吧,你将获得
  • 浏览更多精彩评论
  • 和开发者讨论交流,共同进步

为你推荐

API性能调优
8
8