【架构设计】如何让你的应用做到高内聚、低耦合?

news2024/12/27 11:50:25

前言

最近review公司的代码,发现代码耦合程度特别高,修改一处,不知不觉就把其他地方影响到了,这就让我思考该如何让我们写的代码足够内聚,减少耦合呢?

"高内聚、松耦合"是一个非常重要的设计思想,能够有效地提高代码的可读性和可维护性,缩小功能改动导致的代码改动范围。它可以用来指导不同粒度代码的设计与开发,比如系统、模块、类,甚至是函数,也可以应用到不同的开发场景中,比如微服务、框架、组件、类库等。本文我们来探讨下如何让我们的应用做到高内聚、低耦合。

什么是高内聚?

首先我们将目光投射到内聚上,通常我们的代码的内聚归为7类,如下图所示,内聚性从高到低。

功能内聚

将相同功能放到一个类或者模块中,内聚程度最高。

顺序内聚

如果一个功能的输出是另外一个功能的输入,这种存在顺序依赖关系的,我们将他们归到一个模块中叫做顺序内聚。

通信内聚

如果功能点使用相同输入或输出数据,我们将他们内聚到一个模块中,叫做通信内聚。

过程内聚

如果不同的功能是由同一个控制流支配的,我们称作过程内聚。

时间内聚

不同的功能在同一时间段内执行,比如银行不同的跑批任务,由时间去控制是否放在同一个模块中的内聚叫做时间内聚。

逻辑内聚

不同的功能可能他们内部的逻辑是一致的,我们将他归在一起叫做逻辑内聚。

偶然内聚

而偶然内聚是指不同的功能,没什么关联就直接放在一起了。这种情况很常见,比如A团队同时承担了支付、物流、产品等功能的开发,为了节省开发资源,他们就把不相干的功能放到一个模块中,那么这种偶然内聚会随着业务发展遇到各种问题。

上面讲解了内聚的可能7种分类,大家不用太较真,了解一下就行。

小结一下,我们所说的高内聚,其实是用来指导类或者模块本身的设计,简单来说,就是指相近的功能应该放到同一个类中,不相近的功能不要放到同一个类中。相近的功能往往会被同时修改,放到同一个类中,修改会比较集中,代码容易维护。

什么是低耦合?

现在我们聚焦到耦合上,耦合实际上关注在类与类之间或者模块与模块之间依赖关系的设计,我们通常有下面7种类型的耦合关系。

非直接耦合

两个模块之间没有直接关系,模块独立性最强

数据耦合

两个模块之间用参数传递关联,模块之间影响最小的耦合关系。比如订单系统输入物流编号ID,物流系统返回你具体的物流信息。

标记耦合

两个模块依赖同样的一个数据结构,传递的是数据结构。

比如租房费用计算系统,需要计算水费和电费,如果你传用户信息这个数据结构,让水费和电费系统领取用户对象的用水量和用电量,这就是标记耦合。更好的做法应该只需要传必要的用水量和用电量,而不是传输整个用户对象。

控制耦合

两个模块之间传输控制信息,比如某个标志或者开关,调用模块需要知道被调用模块的内部逻辑,增加了相互依赖和理解的复杂度。

外部耦合

一组模块需要与外部环境关联,这组模块访问同一全局变量,外部耦合有时候必不可少,但应尽量减少此类模块数量。

公共耦合

一组模块均访问同一全局数据区,这种情况叫做公共耦合。比如有一个公共变量,不同模块都去修改它。

内容耦合

一个模块直接操作或修改另外一个模块的内部书,一个模块不通过正常入口访问另外一个模块,这就是内容耦合,是最糟糕的情况,需要避免。比如直接调用另外一个类的set方法,所以这就要求我们不要无脑的把类的任何属性都加上set方法。

上面大致分享了耦合的7种情况,你们项目属于哪种情况呢?

这边再总结一下, 所谓低耦合是说,在代码中,类与类之间的依赖关系简单清晰。即使两个类有依赖关系,一个类的代码改动不会或者很少导致依赖类的代码改动。

高内聚,低耦合有什么关系?

前面讲解了内聚和耦合的多种情况,那么高内聚、低耦合之间有什么关系呢?

上图中左边部分的代码设计中,类的粒度比较小,每个类的职责都比较单一。相近的功能都放到了一个类中,不相近的功能被分割到了多个类中。这样类更加独立,代码的内聚性更好。一个类的修改,只会影响到一个依赖类的代码改动。我们只需要测试这一个依赖类是否还能正常工作就行了。

上图中右边部分的代码设计中,类粒度比较大,低内聚,功能大而全,不相近的功能放到了一个类中。这就导致很多其他类都依赖这个类。当我们修改这个类的某一个功能代码的时候,会影响依赖它的多个类。我们需要测试这三个依赖类,是否还能正常工作。

所以说, “高内聚”有助于“松耦合”,同理,“低内聚”也会导致“紧耦合”

如何做到高内聚、低耦合呢?

关于如何做到模块或者类的高内聚、低耦合,有一个很经典的指导法则,那就是迪米特法则

迪米特法则是说不该有直接依赖关系的类之间,不要有依赖;有依赖关系的类之间,尽量只依赖必要的接口。换更形象点的说,每个模块只和自己的朋友“说话”(talk),不和陌生人“说话”(talk)。

展开来讲,我们在做设计的时候特别关注下面的一些要点。

  1. 多用接口隐藏实现的细节, 接口是一个契约,相对稳定。
  2. 尽量避免不同模块或者类之间共享全局变量,万一一个地方需要修改,连带的其他模块也会影响
  3. 能用private的地方,坚决不用public, 不要给类的任何属性都加set方法,体现良好的封装性,暴露越少,影响也就越少。
  4. 合理使用设计模式,因为设计模式会很好的保证代码的可扩展性、封装性
  5. 注意分层设计和调用,比如在业务层直接用SQL语句操作数据库,而应该讲数据库操作封装到DAO层中,业务层调用DAO层
  6. 避免直接操作或者调用其他模块或类(内容耦合),也就是直接调用set方法,而是应该通过接口调用
  7. 尽量使用数据耦合,少用控制耦合。比如传输一个普通的数据变量是合适的。但传输一个控制命令给你,让你去执行分支a还是分支b还是分支c,这就不是很合适,所以尽量采用一些其他的方法来规避掉这些控制命令的发送。
  8. 模块的功能划分尽可能的单一, 遵循单一职责原则
  9. 模块只堆外暴露最小限度的接口,遵循接口隔离原则
  10. 模块之间的交互要尽量少,接口的设计要尽量简单,比如能传基本类类型就不要传输整个对象。

总结

“高内聚、松耦合”是一个非常重要的设计思想,能够有效提高代码的可读性和可维护性,缩小功能改动导致的代码改动范围。“高内聚”用来指导类本身的设计,“松耦合”用来指导类与类之间依赖关系的设计。

所谓高内聚,就是指相近的功能应该放到同一个类中,不相近的功能不要放到同一类中。相近的功能往往会被同时修改,放到同一个类中,修改会比较集中。所谓松耦合指的是,在代码中,类与类之间的依赖关系简单清晰。即使两个类有依赖关系,一个类的代码改动也不会或者很少导致依赖类的代码改动。

所以我们在设计的时候,尽量要多动动脑子,想想这个功能是不是属于这个模块的呢?他们之间的交互合理吗?复杂吗?有没有更简单的方式呢?多问自己几个问题,你做出的设计会越来越优秀。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/144320.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Idea插件之日志管理神器(Grep Console)

1.简介Grep Console是一款方便开发者对idea控制台输出日志进行个性化管理的插件。2.功能特性Grep Console的主要功能特性:支持自定义规则来过滤日志信息;支持不同级别的日志的输出样式的个性化配置;总结:通过过滤功能、输出日志样…

经典卷积神经网络-AlexNet

AlexNet 学习目标 知道AlexNet网络结构能够利用AlexNet完成图像分类2012年,AlexNet横空出世,该模型的名字源于论文第一作者的姓名Alex Krizhevsky 。AlexNet使用了8层卷积神经网络,以很大的优势赢得了ImageNet 2012图像识别挑战赛。它首次证…

为何瑞达利欧的《原则一》这么难读懂?

开始搞不懂,为何一个桥水基金创始人,一位投资人,却写了一本这样的书,书中的内容初看时觉得与他从事的投资事业几乎毫无关系? 《原则》其副标题为《生活和工作的原则》 乍看,此书黑色的封皮,让我…

【自学C++】C++ std命名空间

C std命名空间 C std命名空间教程 在 C 中 std 命名空间 是 C 中标准库类型对象的命名空间。我们常用的输入和输出 函数 都是定义在 std 命名空间中的,因此,我们需要使用输入和输出,必须要引入 std 命名空间。 要引用一个命名空间中的内容…

电脑自动删除文件怎么恢复?分享4种方法

电脑出现文件丢失的情况常有发生,但是出现电脑自动删除文件的情况是怎么回事呢?电脑自动删除的文件怎么恢复呢?本文将详细阐述电脑自动删除文件原因和文件恢复方法。一、电脑自动删除文件是什么原因1.可能不是删除而是电脑开机用户名更改后导…

Java真的不难(五十三)Docker的快速入门及使用

Docker的入门及使用 这篇文章将不全面介绍理论,Docker对于我们后端开发来说会用就行,能使用Docker去安装一些镜像运行,为简化配置节省时间和错误率,所以这篇文章实用性很高,可以直接上手! 一、什么是Docke…

生产制造业ERP管理系统财务管理解决方案

对于生产制造型企业来说,良好的资金运营管理机制是企业长期、稳定、健康发展的保证。因此,企业急需借助生产制造业ERP管理系统,不断加强企业财务管理,从而有效提升企业的经营效率,降低财务风险,缓解资金成本…

云渲染答疑:动画渲染价格一般多少?

云渲染是什么?云渲染就是通过互联网将用户本地需要渲染的文件上传到云端服务器中,再通过云端庞大的计算机集群资源进行运算操作,帮助用户在云端完成渲染工作后,用户再下载到本地的过程,整个过程操作十分简便。云渲染动…

【云原生进阶之容器】第二章Controller Manager原理2.5节--DeltaFIFO剖析

5 DeltaFIFO DeltaFIFO是K8s中用来存储处理数据的Queue,相较于传统的FIFO,它不仅仅存储了数据保证了先进先出,而且存储有K8s 资源对象的类型,它的作用是保证Reflector和Indexer之间对象同步。其是连接Reflector(生产者)和indexer(消费者)的重要通道。其核心处理流程如下: …

android 换肤框架搭建及使用 (3 完结篇)

本系列计划3篇: Android 换肤之资源(Resources)加载(一)setContentView() / LayoutInflater源码分析(二)换肤框架搭建(三) — 本篇 tips: 本篇只说实现思路,以及使用,具体细节请下载代码查看! 本篇实现效果: fragment换肤recyclerView换肤自定义view属性换肤打开打开打开动…

解决第三方图片403问题

第三方平台怎么处理图片资源保护的? 服务端一般使用 Referer 请求头识别访问来源,然后处理资源访问。 Referer 是什么东西? 扩展参考: http://www.ruanyifeng.com/blog/2019/06/http-referer.html Referer是 HTTP 请求头的一部分,当浏览器向 Web 服务…

HTML实现舔狗日记

演示 css html, body {background: radial-gradient(#181818, #000000);margin: 0;padding: 0;border: 0;-ms-overflow-style: none;}::-webkit-scrollbar {width: 0.5em;height: 0.5em;background-color: #c7c7c7;}/*定义滚动条轨道 内阴影圆角*/::-webkit-scrollbar-track {…

不会写代码?也不懂技术?3分钟搭建电商cps系统搞副业

大家好,我是小悟 唠唠家常 以前见面聊天,大家都习惯性会问“你吃饭了吗”,现在大家一出口就是“你阳了吗”。2023年元旦过去了,你还阳着么?不出意外的话就会出意外,小悟也已经中招过了,在家躺…

【Linux】tcpdump命令详解

1、列出本机所有的网卡接口 tcpdump -D2、捕获特定网口的数据包 tcpdump -i bond0.1083、捕获具体数量的数据包 tcpdump -c 5 -i eth04、捕获的数据包保存到指定的文件 tcpdump -w 0001.pcap -i eth05、捕获的数据包显示IP而不

E4402B频谱分析仪

18320918653 E4402B E4402B|Agilent|3G|频谱分析仪|安捷伦|9kHz至3GHz 品牌:安捷伦 Agilent 惠普 HP 测量速度:28次更新/秒 测量精度:1dB 可选用的10Hz分辨事宽滤波器 机箱可容纳6插槽选件卡 97dB三阶动态范围 能在现场使用的坚固&a…

(1分钟速览)SLAM问题中一般方程和超定方程的求解

今天在学习的过程中偶然看到了一个博客,总结Axb的,那么我也写一篇。首先就是判断A的秩和(A|b)的秩之间的关系,然后通过这个关系来进行进一步地判断。编辑切换为居中添加图片注释,不超过 140 字(可选)求解方…

RabbitMQ通配符模式

🍁博客主页:👉不会压弯的小飞侠 ✨欢迎关注:👉点赞👍收藏⭐留言✒ ✨系列专栏:👉Linux专栏 🔥欢迎大佬指正,一起学习!一起加油! 目录&…

Jenkins安装方式之war包及相关环境配置

持续创作,加速成长!这是我参与「掘金日新计划 10 月更文挑战」的第4天,点击查看活动详情 最近总有小伙伴发私信问我jenkins如何以war形式运行?以及运行后如何添加相关的环境配置,这里我就给大家贴出我的解决方案&…

Bandit算法学习[网站优化]04——UCB(Upper Confidence Bound) 算法

Bandit算法学习[网站优化]04——UCB(Upper Confidence Bound) 算法 参考资料 White J. Bandit algorithms for website optimization[M]. " O’Reilly Media, Inc.", 2013.https://github.com/johnmyleswhite/BanditsBookUCB算法原理及其在星际争霸比赛中的应用Aue…

Springboot 接口为null的值不返回对应的key

偶然听到两个应届生一段对话,一个后端,一个前端 。 前端: 大哥,你没有值就不要返回那个key行不行? 后端: 什么我看看。 后端: 这是本来返回值实体有的,不是必填,所以n…