【程序设计】一文讲解程序设计目标:高内聚,低耦合
前言
软件设计的目标是高内聚、低耦合。
如果代码是高耦合和低内聚的,就会出现修改一个逻辑,会导致多处代码要修改,可能影响到多个业务链路,这增加了出bug的业务风险,同时增加了测试回归的范围,导致研发成本增加。
耦合和内聚,是我们常挂在嘴边的话,但是大家却说不太清楚,讲不太明白,很难衡量:
- 什么样的叫高内聚,什么样的叫低耦合?
- 高内聚要高到什么程度,低耦合要低到什么程度?
3.1 耦合的类型

耦合是描述模块(系统/模块/类/函数)之间相互联系(控制/调用/数据传递)紧密程度的一种度量。
- 紧耦合:模块之间联系越紧密,耦合性就越强,模块的独立性则越差;
- 松耦合:模块之间联系越松散,单个模块解决问题的目的越明确,模块的独立性越强。
✪ 3.1.1 非直接耦合(Nondirect Coupling)

如果两个模块之间没有直接关系,它们之间的联系完全是通过主模块控制调用来实现的,这就是非直接耦合,这种耦合的模块独立性最强。
class User {long userId;String userNick;
}class MessageService {void pushMessage(long userId, String message);
}class UserLoginService {void onLoginEvent(long userId) {User user = queryUserById(userId);String message = user.getUserNick() + "登录成功。";messageService.pushMessage(userId, message);}
}
}
✪ 3.1.2 数据耦合(Data Coupling)

如果一个模块访问另一个模块时,彼此之间是通过数据参数(不是控制参数、公共数据结构或外部变量)来交换输入、输出信息的,则称这种耦合为数据耦合,它是较好的耦合形式。
class MessageService {void pushMessage(long userId, String userNick) {String message = userNick + "登录成功。";doPushMessage(userId, message);}
}class UserLoginService {void onLoginEvent(User user) {messageService.pushMessage(user.getUserId(), user.getUserNick());}
}
✪ 3.1.3 印记(引用)耦合(Stamp Coupling)

当模块之间使用复合数据结构进行通信时,就会发生印记耦合。
- 复合数据结构可以是数组、类、结构体、联合体等的引用,通过复合数据结构在模块之间传递的参数,可能会或不会被接收模块完全使用。
class User {long userId;String userNick;// 该属性未被MessageService使用int level;
}class MessageService {void pushMessage(User user) {String message = user.getUserNick() + "登录成功。";doPushMessage(user.getUserId(), message);}
}class UserLoginService {void onLoginEvent(User user) {messageService.pushMessage(user);}
}
印记耦合优点:
- 把模块A的引用一把传递给模块B,模块B只需要接受少量参数,接口说明简单。
印记耦合缺点:
- 不必要的参数:模块B可能只使用了模块A中部分的数据;
- 模块B捆绑了模块A:任何需要用到模块B的地方,都需要先获取到模块A,无法脱离模块A单独使用;
- 修改可能互相影响:修改模块A或模块B,可能导致对方也需要跟着修改,不符合开闭原则。
印记耦合优化:
增加入参数类型,仅传入模块需要的必要数据,如下:

✪ 3.1.4 控制耦合(Control Coupling)

如果一个模块通过传送开关、标志等控制信息,明显地控制选择另一模块的功能,就是控制耦合。
class MessageService {void pushMessage(long userId, bool isNewUser) {if(isNewUser) {doPushMessage(userId, "登录成功。");}}
}class UserLoginService {void onLoginEvent(User user) {messageService.pushMessage(user.getUserId, user.getIsNewUser());}
}
数据耦合和控制耦合的主要区别:
- 在数据耦合中,模块之间的依赖关系非常小,而在控制耦合中,模块之间的依赖关系很高。在数据耦合中,模块之间通过传递数据进行通信,而在控制耦合中,模块之间通过传递模块的控制信息进行通信;
控制耦合优化:
- 把控制的逻辑放在模块A之中,或增加模块C封装控制逻辑,不然模块B只做某一件独立的事情。
✪ 3.1.5 外部耦合(External Coupling)

外部耦合,是指多个模块同时依赖同一个外部因素(IO设备/文件/协议/DB等),如上图所示:外部耦合与与外部设备的通信有关,而不是与公共数据或数据流有关。
一个模块对外部数据或通信协议所做的任何更改都会影响其他模块,可以通过增加中间模块隔离外部变化来降低耦合度,如下:

✪ 3.1.6 共用耦合(Common Coupling)

共用耦合是指不同的模块共享全局数据的信息(全局数据结构、共享的通信区、内存的公共覆盖区)。
public Response loadInitInfo(Request request) {// request&response是Commands的全局数据Response response = new Response();commandExecutor.serial(request, response,orderRenderRateLimitCommand,renderInitResponseCommand,renderEnrichTradeNoCommand,renderEnrichItemCommand,renderEnrichCombinationCommand,renderEnrichPriceCommand);return response;
}
共用耦合的问题:
- 较难控制各个模块对公共数据的存取,容易影响模块的可靠性和适应性;
- 使软件的可维护性变差,若一个模块修改了共用数据,则会影响相关模块;
- 降低了软件的可理解性,不容易清楚知道哪些数据被哪些模块所共享,排错困难。
✪ 3.1.7 内容耦合(Content Coupling)
内容耦合在低级语言(汇编)中出现,高级语言从设计上已避免出现内容耦合。
如果发生下列情形,两个模块之间就发生了内容耦合:
- 一个模块直接访问另一个模块的内部数据;
- 一个模块不通过正常入口而直接转入到另一个模块的内部;
- 两个模块有一部分代码重叠(该部分代码具有一定的独立功能);
- 一个模块有多个入口。
3.2 内聚的类型

内聚,是描述一个模块内各元素彼此结合的紧密程度,是从功能角度来度量模块内的联系。
- 低内聚:模块内的元素的职责相关性低,通常也意味着模块与外部是紧耦合的。
- 高内聚:模块内的元素的职责相关性强,通常也意味着模块与外部是松耦合的。
通常,解决了耦合的问题,就解决了内聚的问题,反之亦然。
✪ 3.2.1 偶然性内聚
偶然内聚,一个模块内的各元素之间没有任何联系,仅是恰好放在同一个模块内,业务的“Util/Helper”类有大量例子。
问题的原因:通常是模块名起的过于抽象,导致不同职责的元素都可以放进去,从而引起了低内聚。
问题的解法:将抽象的模块拆解成多个更小的具体模块,例如RetailTradeHelper可以拆为OrderAmountHelper/OrderPaymentParamHelper。

✪ 3.2.2 逻辑性内聚
逻辑内聚,把几种相关的功能组合在一起,由调用方传入的参数来确定具体执行哪一种功能。
逻辑内聚是一种“低内聚”,某程度上对应了“控制耦合”,它把内部的逻辑处理暴露给了接口之外,当内部逻辑发生变更时,原本无辜的调用方也会受牵连改动。
public void syncOrder(Order order, String dist) {if(dist == "oc") {syncOrder2Oc(order);}if(dist == "mis") {syncOrder2Mis(order);}if(dist == "tp") {syncOrder2Tp(order);}
}
✪ 3.2.3 时间性内聚
时间内聚,指一个模块内的组件除了在同一时间都会被执行外,相互之间没有任何关联。

✪ 3.2.4 过程性内聚
过程内聚,指一个模块内的组件以特定次序被执行,但相互之间没有数据传递。
✪ 3.2.5 通信性内聚
通信内聚,指一个模块内的组件以特定次序被执行,且相互之间传递和操作相同的数据。

✪ 3.2.6 顺序性内聚
顺序内聚,指一个模块内的元素以特定次序被执行,且上一步的输出被下一元素所依赖。

✪ 3.2.7 功能性内聚
功能内聚,指一个模块内所有组件属于一个整体,完成同一个不可切分的功能,彼此缺一不可。
参考资料
- 《阿里技术》-
相关文章:
【程序设计】一文讲解程序设计目标:高内聚,低耦合
前言 软件设计的目标是高内聚、低耦合。 如果代码是高耦合和低内聚的,就会出现修改一个逻辑,会导致多处代码要修改,可能影响到多个业务链路,这增加了出bug的业务风险,同时增加了测试回归的范围,导致研发成…...
nginx mirror代码分析
实现方式 mirror逻辑的工作阶段: ngx在log phase之后(在ngx_http_free_request处调用)已完成向client端返回response,在log phase之后完成close connection(短链接),在该阶段处理mirror逻辑不…...
Python代理模式介绍、使用
一、Python代理模式介绍 Python代理模式(Proxy Pattern)是一种结构型设计模式。在代理模式中,代理对象充当了另一个对象的占位符,以控制对该对象的访问。 代理对象和被代理对象实现了相同的接口,因此它们可以互相替代…...
《MySQL45讲》笔记—索引
索引 索引是为了提高数据查询效率,就像书的目录一样。如下图,索引和数据就是位于存储引擎中: 索引常见模型 哈希表 以键值对存储的数据结构。适用于只有等值查询的场景。 有序数组 在等值查询和范围查询场景中性能都特别优秀。但是有…...
Android usb host模式通信示例
当使用Android设备作为USB主机时,可以使用Android提供的USB API来进行USB通信。下面是一个简单的Android USB通信的示例。在这个示例中,我们将发送一条消息到连接的USB设备并从USB设备接收响应。 首先,在AndroidManifest.xml文件中添加以下权…...
开源Blazor UI组件库精选:让你的Blazor项目焕然一新!
今天给大家推荐一些开源、美观的Blazor UI组件库,这些优秀的开源框架和项目不仅能够帮助开发者们提高开发效率,还能够为他们的项目带来更加丰富的用户体验。 注:排名不分先后,都是十分优秀的开源框架和项目 Ant Design Blazor…...
MATLAB RANSAC圆柱体点云拟合 (28)
MATLAB RANSAC圆柱体点云拟合 (28) 一、算法介绍二、函数介绍三、算法实现四、效果展示一、算法介绍 RANSAC拟合方法,从原始点云中拟合具有特定形状的点云,这里对原始点云中大致呈圆柱的点云进行分割,圆柱的半径,以及朝向都是比较重要的定义圆柱的参数。下面是具体使用的…...
【AI】《动手学-深度学习-PyTorch版》笔记(七):自动微分
AI学习目录汇总 1、什么是自动微分 自动微分:automatic differentiation,深度学习框架通过自动计算导数,即自动微分,自动微分使系统能够随后反向传播梯度。 计算图:computational graph,根据设计好的模型,系统会构建一个计算图, 来跟踪计算是哪些数据通过哪些操作组合…...
vuejs源码阅读之代码生成器
代码生成器是模版编译的最后以后,它的作用是将AST转换成渲染函数中的内容,这个内容可以称为代码字符串。 代码字符串可以被包装在函数中执行,这个函数就是我们通常说的渲染函数。 渲染函数被执行之后,可以生成一份VNode…...
【MySQL】视图(十)
🚗MySQL学习第十站~ 🚩本文已收录至专栏:MySQL通关路 ❤️文末附全文思维导图,感谢各位点赞收藏支持~ 一.引入 视图(View)是一种虚拟存在的表。视图中的数据并不在数据库中实际存在,行和列数据…...
面试手写实现Promise.all
目录 前言常见面试手写系列Promise.resolve 简要回顾源码实现Promise.reject 简要回顾源码实现Promise.all 简要回顾源码实现Promise.allSettled 简要回顾源码实现Promise.race 简单回顾源码实现结尾 前言 (?﹏?)曾经真实发生在一个朋友身上的真实事件,面试官让…...
TCP网络通信编程之字符流
【案例1】 【题目描述】 【 注意事项】 (3条消息) 节点流和处理流 字符处理流BufferedReader、BufferedWriter,字节处理流-BufferedInputStream和BufferedOutputStream (代码均正确且可运行_Studying~的博客-CSDN博客 1。这里需要使用字符处理流,来将…...
佰维存储面向旗舰智能手机推出UFS3.1高速闪存
手机“性能铁三角”——SoC、运行内存、闪存决定了一款手机的用户体验和定位,其中存储器性能和容量对用户体验的影响越来越大。 针对旗舰智能手机,佰维推出了UFS3.1高速闪存,写入速度最高可达1800MB/s,是上一代通用闪存存储的4倍以…...
降龙十八掌
目录 大数据: 1 HIVE: 1.1 HIVE QL 1.1.1 创建表 1.1.2 更新表 1.1.3 常用语句 1.2 hive参数配置 大数据: 1 HIVE: 1.1 HIVE QL DDL中常用的命令有:create,drop,alter,trunc…...
【项目设计】MySQL 连接池的设计
目录 👉关键技术点👈👉项目背景👈👉连接池功能点介绍👈👉MySQL Server 参数介绍👈👉功能实现设计👈👉开发平台选型👈👉MyS…...
Ubuntu系统adb开发调试问题记录
Ubuntu系统adb开发调试问题记录 一、adb devices no permissions二、自定义adb server端口三、动态库目录四、USB抓包 一、adb devices no permissions lsusb -t 设备树直观地查看设备的Bus ID和Device Num,lsusb找到对应的PID和VID编辑udev规则 sudo vim /etc/ud…...
【宏定义】——检验条件是否成立,并返回指定的值
文章目录 功能说明实现示例解析扩展 功能说明 宏检验条件是否成立,并返回指定的值 #define TU_VERIFY(...) _GET_3RD_ARG(__VA_ARGS__, TU_VERIFY_2ARGS, TU_VERIFY_1ARGS, UNUSED)(__VA_ARGS__)TU_VERIFY(1) 检验为真,啥也不干TU_VERIFY(0) 校验为假&…...
UE5引擎源码小记 —反射信息注册过程
序 最近看了看反射相关的知识,用不说一点人话的方式来说,反射是程序在运行中能够动态获取修改或调用自身属性的东西。 一开始我是觉得反射用处好像不大,后续查了下一些反射的使用环境,发现我格局小了,我觉得用处不大的…...
Redis缓存预热
说明:项目中使用到Redis,正常情况,我们会在用户首次查询数据的同时把该数据按照一定命名规则,存储到Redis中,称为冷启动(如下图),这种方式在一些情况下可能会给数据库带来较大的压力…...
Android 耗时分析(adb shell/Studio CPU Profiler/插桩Trace API)
1.adb logcat 查看冷启动时间和Activity显示时间: 过滤Displayed关键字,可看到Activity的显示时间 那上面display后面的是时间是指包含哪些过程的时间呢? 模拟在Application中沉睡1秒操作,冷启动情况下: 从上可知&…...
【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型
摘要 拍照搜题系统采用“三层管道(多模态 OCR → 语义检索 → 答案渲染)、两级检索(倒排 BM25 向量 HNSW)并以大语言模型兜底”的整体框架: 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后,分别用…...
突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合
强化学习(Reinforcement Learning, RL)是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程,然后使用强化学习的Actor-Critic机制(中文译作“知行互动”机制),逐步迭代求解…...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...
【python异步多线程】异步多线程爬虫代码示例
claude生成的python多线程、异步代码示例,模拟20个网页的爬取,每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程:允许程序同时执行多个任务,提高IO密集型任务(如网络请求)的效率…...
python报错No module named ‘tensorflow.keras‘
是由于不同版本的tensorflow下的keras所在的路径不同,结合所安装的tensorflow的目录结构修改from语句即可。 原语句: from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后: from tensorflow.python.keras.lay…...
IP如何挑?2025年海外专线IP如何购买?
你花了时间和预算买了IP,结果IP质量不佳,项目效率低下不说,还可能带来莫名的网络问题,是不是太闹心了?尤其是在面对海外专线IP时,到底怎么才能买到适合自己的呢?所以,挑IP绝对是个技…...
从面试角度回答Android中ContentProvider启动原理
Android中ContentProvider原理的面试角度解析,分为已启动和未启动两种场景: 一、ContentProvider已启动的情况 1. 核心流程 触发条件:当其他组件(如Activity、Service)通过ContentR…...
前端打包工具简单介绍
前端打包工具简单介绍 一、Webpack 架构与插件机制 1. Webpack 架构核心组成 Entry(入口) 指定应用的起点文件,比如 src/index.js。 Module(模块) Webpack 把项目当作模块图,模块可以是 JS、CSS、图片等…...
SpringSecurity+vue通用权限系统
SpringSecurityvue通用权限系统 采用主流的技术栈实现,Mysql数据库,SpringBoot2Mybatis Plus后端,redis缓存,安全框架 SpringSecurity ,Vue3.2Element Plus实现后台管理。基于JWT技术实现前后端分离。项目开发同时采 …...
Java严格模式withResolverStyle解析日期错误及解决方案
在Java中使用DateTimeFormatter并启用严格模式(ResolverStyle.STRICT)时,解析日期字符串"2025-06-01"报错的根本原因是:模式字符串中的年份格式yyyy被解释为YearOfEra(纪元年份),而非…...
