当前位置: 首页 > news >正文

【程序设计】一文讲解程序设计目标:高内聚,低耦合

前言

软件设计的目标是高内聚、低耦合

如果代码是高耦合和低内聚的就会出现修改一个逻辑,会导致多处代码要修改,可能影响到多个业务链路,这增加了出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&#xf…...

【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秒操作,冷启动情况下: 从上可知&…...

Linux云原生安全:零信任架构与机密计算

Linux云原生安全:零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言:云原生安全的范式革命 随着云原生技术的普及,安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测,到2025年,零信任架构将成为超…...

JDK 17 新特性

#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持,不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的&#xff…...

【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具

第2章 虚拟机性能监控,故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令:jps [options] [hostid] 功能:本地虚拟机进程显示进程ID(与ps相同),可同时显示主类&#x…...

springboot整合VUE之在线教育管理系统简介

可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生,小白用户,想学习知识的 有点基础,想要通过项…...

AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别

【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而,传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案,能够实现大范围覆盖并远程采集数据。尽管具备这些优势&#xf…...

计算机基础知识解析:从应用到架构的全面拆解

目录 前言 1、 计算机的应用领域:无处不在的数字助手 2、 计算机的进化史:从算盘到量子计算 3、计算机的分类:不止 “台式机和笔记本” 4、计算机的组件:硬件与软件的协同 4.1 硬件:五大核心部件 4.2 软件&#…...

解析奥地利 XARION激光超声检测系统:无膜光学麦克风 + 无耦合剂的技术协同优势及多元应用

在工业制造领域,无损检测(NDT)的精度与效率直接影响产品质量与生产安全。奥地利 XARION开发的激光超声精密检测系统,以非接触式光学麦克风技术为核心,打破传统检测瓶颈,为半导体、航空航天、汽车制造等行业提供了高灵敏…...

HybridVLA——让单一LLM同时具备扩散和自回归动作预测能力:训练时既扩散也回归,但推理时则扩散

前言 如上一篇文章《dexcap升级版之DexWild》中的前言部分所说,在叠衣服的过程中,我会带着团队对比各种模型、方法、策略,毕竟针对各个场景始终寻找更优的解决方案,是我个人和我司「七月在线」的职责之一 且个人认为&#xff0c…...

数据结构:泰勒展开式:霍纳法则(Horner‘s Rule)

目录 🔍 若用递归计算每一项,会发生什么? Horners Rule(霍纳法则) 第一步:我们从最原始的泰勒公式出发 第二步:从形式上重新观察展开式 🌟 第三步:引出霍纳法则&…...

渗透实战PortSwigger Labs指南:自定义标签XSS和SVG XSS利用

阻止除自定义标签之外的所有标签 先输入一些标签测试&#xff0c;说是全部标签都被禁了 除了自定义的 自定义<my-tag onmouseoveralert(xss)> <my-tag idx onfocusalert(document.cookie) tabindex1> onfocus 当元素获得焦点时&#xff08;如通过点击或键盘导航&…...