文章目录
- 1 前言
- 2 低耦合
- 2.1 耦合性定义
- 2.2 耦合性分类
- 2.3 使用低耦合
- 3 高内聚
- 3.1 高内聚定义
- 3.2 内聚性分类
- 4 示例
- 5 高内聚,低耦合的系统有什么好处呢
- 6 降低耦合度的方法
- 7 总结
- 8 结语
1 前言
编程的时候,经常要使得系统高内聚、低耦合,那么,我们口中的这些概念到底是什么?什么是高内聚低耦合呢?
下面将简单介绍下高内聚和低耦合,先贴一个网站摘来的美图。
2 低耦合
2.1 耦合性定义
- 起因
模块独立性指每个模块只完成系统要求的独立子功能,并且与其他模块的联系最少且接口简单,所以就有了两个定性的度量标准,即耦合性
和内聚性
。 - 定义:
耦合性也称块间联系,指软件系统结构中各模块间相互联系紧密程度的一种度量。使用白话来说,就是代码(程序)和代码(程序)之间的关联程度
。
模块之间联系越紧密,其耦合性就越强,模块的独立性则越差。模块间耦合高低取决于模块间接口的复杂性、调用的方式及传递的信息
。
2.2 耦合性分类
耦合性分类(从左至右,耦合性依次从低–>高): 无直接耦合
、数据耦合
、标记耦合
、控制耦合
、公共耦合
、内容耦合
- 无直接耦合
两个模块之间没有直接关系,它们之间的联系完全是通过主模块的控制和调用来实现的。耦合度最弱,模块独立性最强。 - 数据耦合
指两个模块之间有调用关系,传递的是简单的数据值,相当于高级语言的值传递。 - 标记耦合
指两个模块之间传递的是数据结构,如高级语言中的数组名、记录名、文件名等这些名字即标记,其实传递的是这个数据结构的地址。 - 控制耦合
指一个模块调用另一个模块时,传递的是控制变量(如开关、标志等),被调模块通过该控制变量的值有选择地执行块内某一功能。 - 公共耦合
指通过一个公共数据环境相互作用的那些模块间的耦合。公共耦合的复杂程序随耦合模块的个数增加而增加。 - 内容耦合
这是最高程度的耦合,也是最差的耦合。当一个模块直接使用另一个模块的内部数据,或通过非正常入口而转入另一个模块内部。
2.3 使用低耦合
- 一个完整的系统,模块与模块之间,
尽可能的使其独立存在
。也就是说,让每个模块,尽可能的独立完成某个特定的子功能。模块与模块之间的接口,尽量的少而简单。 - 如果某两个模块间的关系比较复杂的话,最好首先考虑进一步的模块划分,方便使用依赖、组合等,方便以后功能的扩展。
3 高内聚
3.1 高内聚定义
定义:
内聚性又称块内联系,指模块的功能强度的度量,即一个模块内部各个元素彼此结合的紧密程度的度量。若一个模块内各元素(语名之间、程序段之间)联系的越紧密,则它的内聚性就越高
。
所谓高内聚是指一个软件模块是由相关性很强的代码组成,只负责一项任务,也就是常说的单一责任原则。 一个功能可以通过聚合或者组合等关系达到高度内聚,这样即使聚合的内容发生变化了,这里也不用做变动,从而达到轻松扩展。
3.2 内聚性分类
内聚性分类(从左至右,耦合性依次从低–>高): 偶然内聚
、逻辑内聚
、时间内聚
、通信内聚
、过程内聚
、顺序内聚
、功能内聚
- 偶然内聚
指一个模块内的各处理元素之间没有任何联系。 - 逻辑内聚
指模块内执行几个逻辑上相似的功能,通过参数确定该模块完成哪一个功能。 - 时间内聚
把需要同时执行的动作组合在一起形成的模块为时间内聚模块。 - 通信内聚
指模块内所有处理元素都在同一个数据结构上操作或所有处理功能都通过公用数据而发生关联(有时称之为信息内聚)。即指模块内各个组成部分都使用相同的数据结构输入或产生相同的数据输出。 - 过程内聚
构件或者操作的组合方式是,允许在调用前面的构件或操作之后,马上调用后面的构件或操作,即使两者之间没有数据进行传递。简单的说就是如果一个模块内的处理元素是相关的,而且必须以特定次序执行则称为过程内聚。
例如: 某要完成登录的功能,前一个功能判断网络状态,后一个执行登录操作,显然是按照特定次序执行的。 - 顺序内聚
指一个模块中各个处理元素都密切相关于同一功能且必须顺序执行,前一功能元素输出就是下一功能元素的输入。
例如: 某要完成获取订单信息的功能,前一个功能获取用户信息,后一个执行计算均价操作,显然该模块内两部分紧密关联。
顺序内聚的内聚度比较高,但缺点是不如功能内聚易于维护。 - 功能内聚
模块内所有元素的各个组成部分全部都为完成同一个功能而存在,共同完成一个单一的功能,模块已不可再分。即模块仅包括为完成某个功能所必须的所有成分,这些成分紧密联系、缺一不可,这时候与其他模块的耦合是最弱的。
功能内聚是最强的内聚,其优点是它的功能明确
。判断一个模块是否功能内聚,一般从模块名称就能看出。如果模块名称只有一个动词和一个特定的目标(单数名词),一般来说就是功能内聚,如:“计算水费”、“计算产值”等模块。功能内聚一般出现在软件结构图的较低层次上。
功能内聚模块的一个重要特点是:他是一个“黑盒”,对于该模块的调用者来说,只需要知道这个模块能做什么,而不需要知道这个模块是如何做的。
4 示例
有个例子很容易明白:一个程序有50个函数,这个程序执行得非常好。然而一旦你修改其中一个函数,其他49个函数都需要做修改,这就是高耦合的后果。
一旦你理解了它,你编写概要设计的时候,设计类或者设计模块时,自然会考虑到“高内聚,低耦合”,潜移默化,你就有了这种编程思想。
代码示例:
场景:在一所学校,里面有老师若干名,依次编号。老师下面有学生若干名,依次编号。现在要求打印出所有的老师和学生的ID。
- 违反高内聚低耦合原则的代码
有四个类Teacher、Student 、StudentManage 和TeacherManage
@Getter
@Setter
public class Teacher {
private String id;
}
@Getter
@Setter
public class Student {
private String id;
}
import java.util.ArrayList;
import java.util.List;
public class StudentManage {
public List<Student> getAllStudent() {
List<Student> list = new ArrayList<>();
for (int i = 0; i < 100; i++) {
Student student = new Student();
student.setId("学生学号是" + i);
list.add(student);
}
return list;
}
}
import java.util.ArrayList;
import java.util.List;
public class TeacherManage {
private List<Teacher> getAllTeacher() {
List<Teacher> list = new ArrayList<>();
for (int i = 0; i < 100; i++) {
Teacher teacher = new Teacher();
teacher.setId("老师编号" + i);
list.add(teacher);
}
return list;
}
public void printAllPerson(StudentManage studentManage) {
List<Student> list1 = studentManage.getAllStudent();
for (Student s : list1) {
System.out.println(s.getId());
}
List<Teacher> list2 = this.getAllTeacher();
for (Teacher t : list2) {
System.out.println(t.getId());
}
}
}
测试类:
/**
* 高内聚低耦合测试类
*/
public class TestCoupling {
public static void main(String[] args) {
//现在这个设计的主要问题出现在TeacherManage类中,根据低耦合高内聚法则,Student类与TeacherManage类发生耦合。
TeacherManage teacherManage = new TeacherManage();
teacherManage.printAllPerson(new StudentManage());
}
}
现在这个设计虽然实现了功能,但是设计的有一些问题,主要问题出现在TeacherManage类中,根据高内聚低耦合编程思想,这里TeacherManage类与Studeng类高度耦合了,不方便以后功能的扩展。
- 遵循高内聚低耦合原则的代码
这里把以上老师管理类和学生管理类进行重新设计,每个管理类负责维护专门的类。
import java.util.ArrayList;
import java.util.List;
public class TeacherManageChange {
private List<Teacher> getAllTeacher() {
List<Teacher> list = new ArrayList<>();
for (int i = 0; i < 100; i++) {
Teacher teacher = new Teacher();
teacher.setId("老师编号" + i);
list.add(teacher);
}
return list;
}
public void printAllTeacher() {
List<Teacher> list = this.getAllTeacher();
for (Teacher t : list) {
System.out.println(t.getId());
}
}
}
import java.util.ArrayList;
import java.util.List;
public class StudentManageChange {
public List<Student> getAllStudent() {
List<Student> list = new ArrayList<>();
for (int i = 0; i < 100; i++) {
Student student = new Student();
student.setId("学生学号是" + i);
list.add(student);
}
return list;
}
public void printAllStudent() {
List<Student> list1 = this.getAllStudent();
for (Student s : list1) {
System.out.println(s.getId());
}
}
}
测试用例:
public static void main(String[] args) {
TeacherManageChange teacherManageChange = new TeacherManageChange();
teacherManageChange.printAllTeacher();
StudentManageChange studentManageChange = new StudentManageChange();
studentManageChange.printAllStudent();
}
通过该示例可以很清晰的知道,这个示例中,学生管理类StudentManageChange
管理了学生的编号,老师管理类TeacherManageChange
管理了老师的编号。
这样代码之间的耦合度降低了,而且后续即使需求发生变化,比如说:打印学生编号,又需要打印学生名字,这时候只需要修改学生类和学生管理类,而不用修改老师相关的类,这样不会因为高耦合,牵一发而动全身。所以说,高内聚低耦合的代码,更容易维护。
5 高内聚,低耦合的系统有什么好处呢
- 事实上,短期来看,并没有很明显的好处,甚至短期内会影响系统的开发进度,因为高内聚,低耦合的系统对开发设计人员提出了更高的要求。
高内聚,低耦合的好处体现在系统持续发展的过程中,高内聚,低耦合的系统具有更好的重用性,维护性,扩展性,可以更高效的完成系统的维护开发,持续的支持业务的发展,而不会成为业务发展的障碍
。 - 高内聚低耦合是否意味着内聚越高越好,耦合越低越好?
并不是内聚越高越好,耦合越低越好,真正好的设计是在高内聚和低耦合间进行平衡,也就是说高内聚和低耦合是冲突的
。 - 最强的内聚莫过于一个类只写一个函数,这样内聚性绝对是最高的。但这会带来一个明显的问题:类的数量急剧增多,这样就导致了其它类的耦合特别多,于是整个设计就变成了“高内聚高耦合”了。由于高耦合,整个系统变动同样非常频繁。
- 对于耦合来说,最弱的耦合是一个类将所有的函数都包含了,这样类完全不依赖其它类,耦合性是最低的。但这样会带来一个明显的问题:内聚性很低,于是整个设计就变成了“低耦合低内聚”了。由于低内聚,整个类的变动同样非常频繁。
真正做到高内聚、低耦合是很难的,很多时候未必一定要这样,更多的时候“最适合”的才是最好的,不过、审时度势、融会贯通、人尽其才、物尽其用,才是设计的王道。
6 降低耦合度的方法
(1)少使用类的继承,多用接口隐藏实现的细节。 Java面向对象编程引入接口除了支持多态外, 隐藏实现细节也是其中一个目的。
(2)模块的功能化分尽可能的单一,道理也很简单,功能单一的模块供其它模块调用的机会就少。(其实这是高内聚的一种说法,高内聚低耦合一般同时出现)。
(3)遵循一个定义只在一个地方出现。
(4)少使用全局变量。
(5)类属性和方法的声明少用public,多用private关键字。
(6)多用设计模式,比如采用MVC的设计模式就可以降低界面与业务逻辑的耦合度。
(7)尽量不用“硬编码”的方式写程序,同时也尽量避免直接用SQL语句操作数据库。
(8)最后当然就是避免直接操作或调用其它模块或类(内容耦合);
(9)如果模块间必须存在耦合,原则上尽量使用数据耦合,少用控制耦合,
(10)限制公共耦合的范围,避免使用内容耦合。
7 总结
- 内聚和耦合分别描述的是模块内部特征,和模块外部引用关系。
- 内聚就是一个模块内各个元素彼此结合的紧密程度,高内聚就是一个模块内各个元素彼此结合的紧密程度高。
内聚是就其中任何一个模块的内部特征而言的。 - 耦合是系统结构中各模块间相互联系紧密程度的一种度量。使用白话来说,就是代码(程序)和代码(程序)之间的关联程度。
耦合是就多个模块组成的系统中各个模块的关联关系而言的。
8 结语
无论如何,不要把功能写的很复杂,别人看不懂,要善与划分好模块,合适的使用依赖、组合等关系降低功能或者类之间的关联程度(低耦合度)。
尽可能遵循开闭原则(OCP)原则,编程时脑海里想着“高内聚、低耦合”就行啦。
高内聚是说模块内部要高度聚合,低耦合是说模块与模块之间的藕合度要尽量低。前者是说模块内部的关系,后者是说模块与模块间的关系
。
耦合性与内聚性是模块独立性的两个定性标准,将软件系统划分模块时,尽量做到高内聚低耦合,提高模块的独立性,为设计高质量的软件结构奠定基础
。