【AICon】 如何构建高效的 RAG 系统?RAG 技术在实际应用中遇到的挑战及应对策略?>>> 了解详情
写点什么

抽象、低内聚、难变更,你还在用“堆栈”组织代码?

Kislay Verma

  • 2021-08-23
  • 本文字数:2231 字

    阅读完需:约 7 分钟

抽象、低内聚、难变更,你还在用“堆栈”组织代码?

在企业代码库中,目前最流程的代码组织方式是按照技术栈的层次对所有类进行分组,即“堆栈”(stack)风格。但这种风格存在抽象不恰当、低内聚、难变更及设计选择受限等问题,从而作者提出了一种替代方案,“实体”风格的代码组织方式。


在企业代码库中,你遇到的最流行的代码组织方式是什么样的?我最常见到的一种方式是按照技术栈的层次对所有类(假设是 Java 领域)进行分组。所以在一个 MVC 风格的系统中,所有的控制器都在一起,所有的服务都在一起,所有的存储层都在一起,所有的 POJO 也都在一起,等等。我们把这种代码组织方式称为“堆栈”(stack)风格



这是一种糟糕的代码组织方式,我将会在下面解释原因。但首先,我会先提供一种替代方案。

 

一种更好的代码组织方式是,根据代码所表示的逻辑实体对其进行分组。我们称这种代码组织方式为“实体”(entity)风格。 其目的是确保与单一概念相关的所有类都聚集在一起。 通过将逻辑实体放在首位,我们可以优化人的理解能力(编译器才不在乎你把对应的类放在哪里呢)。通过代码本身的呈现方式,开发人员可以对实际系统的边界做出更明智的选择。 不是将概念界定在 SomethingRepository 和 SomethingElseRepository 之间,而是界定在 Something 和 SomethingElse 之间。



现在,我将解释下为什么我认为实体模型比堆栈模型更好。

抽象不恰当


人们不会按堆栈的层次来阅读代码。没有人会说“给我展示下这个系统所有的 API”或“给我展示下这个系统触发的所有查询”。人们沿着领域边界阅读代码。在酒店管理系统中,人们考虑的是房间、客人和价格等等。

 

由于“堆栈”风格的代码是按照技术层组织的,因此通过系统在存储库中的存在方式来理解系统的逻辑模型。 “堆栈”风格暴露的边界是技术层。我们无法从这段代码中理解“名词”以及它们之间的关系。必须还得再深挖一层。对于一个刚开始阅读代码的新人来说,这种“逻辑”结构的混淆是一个巨大的分歧点。

 

在我们的酒店管理示例中,“实体”风格将所有与客人相关的代码(无论技术层如何)放在一个包中,所有与房间相关的代码放在另一个包中,依此类推。这些包中的每一个都可以有自己的“堆栈”风格的内部组织结构,或者是几个处于同一级别的类。这样可以很容易地在一个地方找到与客人有关的所有内容。

低内聚


“堆栈”风格组织方式的另一个常见论点是,它将独立的模块放在了技术栈的不同层中。 例如,控制器与服务层、服务层与存储层等是明显分离的。在技术栈的不同层次上查找类,你需要转到对应的层次包中。 这鼓励了不同层之间的解耦。



这个论点的问题在于,它只关注了耦合,而忽略了另一个关键属性——内聚。我们希望在哪些类之间增加内聚,哪些类之间减少耦合呢?既然所有的服务层都在一起,那么我们是否可以说,它们具有高内聚,并且与模型类或存储库之间是解耦的呢?我们是否可以让所有存储库高度依赖彼此,但与服务层的业务逻辑解耦呢?显然答案是否定的!这种代码将是教科书级的“大泥潭”。将这种系统重构为更小的系统绝对会是一场噩梦,因为你必须要在技术栈的每一层中解耦类。它违背了使用 MVC 风格的全部目的。

 

另一方面,“实体”风格,促进了内聚,同时仍为技术栈风格的解耦留出了空间。如果所有与酒店相关的类都相互依赖(技术上或概念上的),这是可以的,因为它们无论如何都是一个单一的工作单元。它还能使得以后的重构更容易,因为它的逻辑边界比“堆栈”风格更清晰。


难变更

 

在以“堆栈”风格组织的代码库中,开发人员进行任何有意义的变更,都必须跨越多个包进行编码。例如,要在一个实体及其 CRUD API 中添加新字段,需要修改所有的包。这会产生认知负载,因为开发人员必须修改许多“事物”,而不是一个单一的逻辑事物。

 

在“实体”风格中,如果你要变更某个对象,只需在一个逻辑边界内进行更改即可。这使得对它们的变更更容易,因为如果变更的是单个实体,我们只需处理代码库的一小部分即可。如果更改跨越了顶级包,那么你就是在变更跨越其定义的逻辑结构,这将提醒你,代码存在潜在的耦合风险。

设计选择受限

 

由于代码是按技术栈或功能组织的,它限制了人们对系统设计的思考方式。例如,由于业务逻辑应该放在“服务”中,开发人员拒绝使用适当的设计结构,而宁愿将所有内容都塞进服务中,从而创建了长达数千行的噩梦类。即使他们使用了好的设计原则,代码的组织也会抵制他们,因为每个新的“类型”都必须在一个单一的包中。如果想在不同的服务中使用工厂模式,那么必须开发一个名为 factory 的全新包层次结构,此后所有的工厂都应该聚集在这里,无论它们彼此之间是否有任何关联。

 

如前所述,“实体”并不限定每个逻辑包在其内部的组织方式。它可以是“堆栈”风格,也可以是按需拥有尽可能多的包类型,只要不影响另一个实体包的选择即可。



这里需要关注的一个问题是如何组织跨实体的事物。例如,在多个实体上运行的工作流。这两种风格都没有给出一个简明的答案,但在我看来,“实体”风格在这方面做得更好,因为它会强制要求在所有实体包之外再创建一个新包。这突出了工作流是一个新概念,并且可能是一个应该独立开发的系统边界。其思想是将相似的概念组合在一起,但不受单一概念约束的事物仍然可以在此基础上拥有自己的逻辑家园。

 

我觉得我们对代码组织所提倡的思维模式考虑得还不够。 这类似于代码库级别的康威定律。 我很想听到更多关于如何组织代码,以及它是如何塑造开发人员行为、心理模型或效率的信息。 在评论区留言!

 

原文链接:

https://kislayverma.com/programming/how-to-organize-your-code

2021-08-23 18:354073

评论 5 条评论

发布
用户头像
提出皇帝的新衣也是需要勇气的.
2021-08-30 15:37
回复
用户头像
在上面的例子中,如果hotel和room公用一个DTO,那么这个DTO应该放在哪里呢
2021-08-24 08:23
回复
公用所说的应该是hotel和room的对象的聚合把?,那就是在hotel或者room里面有一层做聚合的服务(如果service职责比较单一,单roo/hotel操作),如果service职责不单一,那就可以放在service层做聚合
2021-08-24 09:15
回复
应该是看DTO是什么东西吧? 或者把DTO放在一起. 牛逼一点的直接上DOP用hashmap
2021-08-30 15:36
回复
DOP 是啥,求一点点的解释
2021-09-08 09:46
回复
没有更多了
发现更多内容

阿里一面,给了几条SQL,问需要执行几次树搜索操作?

Java 程序员 后端

使用Eclipse连接SAP云平台上的HANA数据库实例

Jerry Wang

数据库 Cloud SAP 11月日更

阿里三面面试题:分布式服务注册中心该如何选型?我快哭了

Java 程序员 后端

我终于知道,中国互联网是怎么弯道超车,干翻美国了!

博文视点Broadview

阿里P8大佬终于把自己珍藏多年581页JavaJDK9学习笔记分享出来了

Java 程序员 后端

阿里P8大牛透出,入职阿里必备12套Java面试题,适合从校招到社招

Java 程序员 后端

阿里P8直接甩我一份Java笔记、面试宝典,当我看到目录的一瞬间,简直怀疑人生!

Java 程序员 后端

拥有一台服务器后,我竟然这么酷?

老表

Python Linux web开发 云服务器 跟老表学云服务器

华为全球技术服务引领行业数智创新,共赢未来

安装企业级的dokuwiki文档系统

小鲍侃java

11月日更

阿里P8级大神经验分享,怎样成为一个优秀的架构师?(1)

Java 程序员 后端

阿里一面就凉了:MySQL+多线程+Redis+算法

Java 程序员 后端

用JavaScript访问SAP云平台上的服务遇到跨域问题该怎么办

Jerry Wang

JavaScript SAP 11月日更 SAP微信集成

重走JAVA之路(五):面试又被问线程池原理?教你如何反击

Java 程序员 后端

闲下来的学习时光

Java 程序员 后端

Python爬虫120例之第20例,1637、一路商机网全站加盟数据采集

梦想橡皮擦

11月日更

阿里-美团-字节面试官必问的Mysql锁机制,你真的明白吗

Java 程序员 后端

阿里P8面试官梳理的2020年999道大厂高频Java面试题(附答案)

Java 程序员 后端

阿里Redis最全面试全攻略,读完这个就可以和阿里面试官好好聊聊

Java 程序员 后端

鸿蒙轻内核源码分析:虚拟内存

华为云开发者联盟

鸿蒙 内存 虚拟内存 OpenHarmony 轻内核

阿里Java架构师春招面试高频600题:集合+JVM+Redis+并发

Java 程序员 后端

阿里P8十年摸爬滚打,告诉你上古程序猿为何反对使用Redis?

Java 程序员 后端

阿里P8架构师汇总了史上101个最牛逼的Java技术点

Java 程序员 后端

Vue进阶(幺捌叁):IE9兼容性问题-数据初始化问题

No Silver Bullet

Vue 11月日更

金三银四助力面试-手把手轻松读懂HashMap源码

Java 程序员 后端

阿里P7告诉你SpringBoot如何防止重复提交?

Java 程序员 后端

阿里P8大牛整理的300页图解网络知识+计算机底层操作系统

Java 程序员 后端

阿里2020首发一百多道Java高级岗面试题(含答案)

Java 程序员 后端

闭关2个月肝完Java7大核心知识(分布式+JVM+Java基础+算法

Java 程序员 后端

一个简单的UserCase,带你认识openLooKeng的行级权限控制

openLooKeng

大数据 openLooKeng

阿里P6程序员的Java之路-或许起点不高,但我从未停止我的脚步

Java 程序员 后端

抽象、低内聚、难变更,你还在用“堆栈”组织代码?_语言 & 开发_InfoQ精选文章