耦合(Coupling)和内聚(Cohesion)是衡量模块设计质量的两个非常重要的概念。耦合描述了一个模块与其他模块之间的依赖关系强度,内聚则描述了模块内部各个部分之间的关联程度。
什么是耦合和内聚?
- 内聚:内聚指的是各个模块的内部关系,模块内部功能之间的关系,内聚就是模块内部各元素之间的联系是否紧密,我们所说的低耦合高内聚指的就是模块功能颗粒度尽可能小,通俗来说,如果一个模块的功能能被拆分成很多小功能,那么这个模块的内聚度就不高。
- 耦合:耦合指的是各个模块之间的关系,如果两个模块的功能相互依赖,没有他我就不能做事,说明两个模块的联系非常紧密,依赖度高,耦合度高。
软件开发应向“低耦合高内聚”靠拢,理想的软件设计应该追求低耦合和高内聚。低耦合可以使模块更独立、更易于修改和维护;高内聚则确保模块内部逻辑清晰、专注,更易于理解和复用。
7种耦合
- 内容耦合(Content Coupling):一个模块直接使用另一个模块的内部数据或者控制流程时。比如当一个类直接调用访问另一个类的属性信息。
- 公共耦合(Common Coupling):当两个或更多模块共享同一个全局数据结构或全局变量时,这些模块被认为具有公共耦合。公共环境包括全局变量、共享数据结构、或存储介质上的文件等。模块间通过访问这些共享的数据结构或全局变量进行交互。这种耦合方式比较紧密,因为模块间的共享数据使得它们的依赖关系更加显著。与数据耦合不同,公共耦合涉及的全局数据结构可能会被多个模块同时读写,增加了模块间的耦合度。
- 外部耦合(External Coupling): 当多个模块共享一个外部传来的数据结构或者全局变量时。如 I/O 将模块耦合到特定的设备、格式、 通信协议上。
- 控制耦合(Control Coupling): 当一个模块控制着另一个模块的逻辑流程(比如通过传递控制参数)时。存在流程控制,说明控制参数会决定接下来的流程。典型的例子就是计算电费,有个参数,是计算平均的还是最高的,平局和最高是两个独立的计算模块,因此,这两个计算模块分别和计算电费模块耦合。
- 标记耦合(Stamp Coupling):又叫特征耦合,当把整个数据结构作为参数传递,而被调用的模块只需要使用其中一部分数据元素时,就出现了特征耦合。
public class Main {
private static void 特征耦合(Dog d) {//这里的参数即被“特征耦合”模块引用的Dog模块(类)的数据结构。
System.out.println(d.getName());
}
private static void 非特征耦合(String name) {
System.out.println(name);
}
public static void main(String[] args) {
Dog d = new Dog("旺财");//这里可以看作main模块和Dog模块的数据耦合
特征耦合(d);
非特征耦合(d.getName());
}
}
- 数据耦合(Data Coupling): 当模块间的交互仅仅通过参数来传递基本数据类型时。这是最佳的耦合方式。如上面的赋值操作,或者如下的电费计算。
- 无耦合(No Coupling): 模块之间没有任何直接的关系,是最理想的状态,但在实际应用中很难完全实现。(也经常说6种耦合7种内聚,所以也可以说这不算是一种耦合)
7种内聚
- 偶然内聚(Coincidental Cohesion): 模块内部各个部分之间没有明显的关联,仅仅是偶然放在一起,这是最低级别的内聚。通常会导致代码难以维护和理解。这里提一嘴,我认为代码生成器就是一种偶然内聚,只是因为其他模块都有执行若干不相干的语句,我们将之提取,并作为独立模块。
- 逻辑内聚(Logical Cohesion): 当一个模块中的元素被组织在一起,因为它们在逻辑上分类相似,通常通过一个控制语句(如 if-else 或 switch)来选择执行。一些不相干的功能被组织在同一个模块中,通过外部参数来控制实现哪个功能。简单记为,通过逻辑语句联系在一起的内聚。
- 时间内聚(Temporal Cohesion): 模块中的元素相关联,因为它们需要在同一时间段内被执行。顾名思义,就是单纯在时段上重合,也称为瞬时内聚,关键词就是同时执行。
- 过程内聚(Procedural Cohesion): 当模块中的元素协同工作执行一个任务序列,彼此之间按照特定顺序执行。例如:先写姓名 → 电话 → 家庭住址,先后顺序符合客户需求。关键词就是特定顺序执行。
- 通信内聚(Communication Cohesion): 模块中的所有功能都操作相同的数据结构。即指模块内各个组成部分都使用相同的数据结构或产生相同的数据结构。例如:一个模块根据员工生日计算员工年龄和退休时间。
- 顺序内聚(Sequential Cohesion): 当一个模块内的操作必须按特定顺序执行,输出数据成为下一步操作的输入数据时。前 一功能元素的输出就是下一功能元素的输入。例如:先计算员工的年龄再计算退休时间。例如某要完成获取订单信息的功能,前一个功能获取用户信息,后一个执行计算均价操作,显然该模块内两部分紧密关联。过程内聚与顺序内聚的区别是: 顺序内聚中是数据流从一个处理单元流到另一个处理单元,而过程内聚是控制流从一个动作流向另一个动作。
- 功能内聚(Functional Cohesion): 当一个模块内的所有功能都是围绕一个单一的功能组织的,这是最高级别的内聚。ALL for ONE,所有处理元素只为完成某一个功能,缺一不可。