架构师-高内聚低耦合架构

8320f3f4d23a45d8b306bb5af8db74f3.png

内聚

 

指某一个事物内部各要素之间的紧密联系程度。如果相同的要素越集中,说明内聚越高,反之则越低。比如人身体里的五脏六腑,彼此之间从出生就相互协作,如果换成其他人的器官,就会出现排斥现象,这就是高内聚。所谓耦合,指不同事物之间的依赖性。如果彼此的依赖性越强,说明耦合性越高,反之则越低。还拿人体举例,心脏负责供血,肺负责呼吸,肝脏负责代谢,彼此之间分工明确,各司其职,即便某一部位生病了,只需要针对性治疗,不影响其它部位,这就是低耦合。

33546457636142598494650003624bfc.png

f4f2e2256ab041779590631d322df93d.png

a2a9ef2e3d554a84b94d0692e0b7f9a9.png

549a8d2c7ec7424198a4a56292468932.png

软件模块内聚按高低排列为:

功能内聚

完成一个单一功能,各个部分协同工作,缺一不可。这是最理想的内聚类型,其中模块内的所有元素都共同完成一个单一的功能。例如,一个咖啡机设计的目的就是为了冲泡咖啡,所有部件都是为了这一功能服务的

例:模块A实现将新注册的用户信息(用户名,密码,个性签名)全部转换成String类型并插入数据库。模块A中就是功能内聚。

功能内聚是内聚度最高的一种类型。指模块内的所有元素共同作用完成一个功能,缺一不可。例如,在一个模块中包含了以下三个函数:

void init_database() {
  // 初始化数据库
}

void query_database(char *query) {
  // 查询数据库
}

void close_database() {
  // 关闭数据库
}

顺序内聚

  例:模块A实现将传入的Date类型数据转换成String类型,然后再将转换好的String类型数据插入数据库。模块A中就是顺序内聚。例如,在汽车制造线上,一个工位负责安装轮胎,然后下一个工位进行轮胎检验,每个工位的任务都是顺序进行的。

处理元素相关,而且必须顺序执行。顺序内聚是指一个模块中的各个处理元素都密切相关于同一各功能且必须顺序执行,前一个功能元素的输出就是下一个功能的输入。例如,在一个模块中包含了以下三个函数:

void parse_input(char *input) {
  // 解析输入
}

void process_input(char *input) {
  // 处理输入
}

void generate_output(char *output) {
  // 生成输出
}

通信内聚

所有处理元素集中在一个数据结构的区域上。

  例:模块A实现将传入的Date类型数据转换成String类型,以及将Date类型数据插入数据库,这两个操作都是对“Date类型数据”而言的。模块A中就是通信内聚。例如,一个财务软件模块,包括计算税收、生成财务报表等功能,这些功能都在处理同一批财务数据

通信内聚是指模块内的所有处理元素都在同一数据结构上操作,或者各处理使用相同的输入数据或产生相同的输出数据。例如,在一个模块中包含了以下三个函数:

void add_student(Student *student) {
  // 添加学生信息
}

void delete_student(int id) {
  // 删除学生信息
}

void modify_student(Student *student) {
  // 修改学生信息
}

过程内聚

处理元素相关,而且必须按特定的次序执行。

  例:用户登陆了某某网站,A模块负责依次读取用户的用户名、邮箱和联系方式,这个次序是事先规定的,不能改变。模块A中就是过程内聚。

例如,一个文档处理模块可能包括打开文件、格式化文本和打印文件的功能,这些功能都是按顺序执行的,但并不是为了完成一个独立的功能。

过程内聚是指一个模块完成多个任务,这些任务必须按指定的过程执行。例如,在一个模块中包含了以下三个函数:

void open_file(char *filename) {
  // 打开文件
}

void read_file(char *buffer, int size) {
  // 读取文件
}

void close_file() {
  // 关闭文件
}

瞬时内聚

(时间内聚):所包含的任务必须在同一时间间隔内执行。例如,程序启动时需要执行的一系列初始化功能,如配置内存、加载用户设置等,都是因为它们在相同的时期(启动时)执行

例:编程开始时,程序员把对所有全局变量的初始化操作放在模块A中。模块A中就是时间内聚。

逻辑内聚

完成逻辑上相关的一组任务。逻辑上相关的功能被放在同一模块中。

   例:A模块实现的是将对应的人员信息发送给技术部,人事部和财政部,决定发送给哪个部门是输入的控制标志决定的。模块A中就是逻辑内聚。例如,一个应用程序可以有一个打印模块,用于打印文本文件、图像或PDF,用户选择什么文件类型,就执行相应的打印任务

即逻辑内聚是指模块内执行若干个逻辑上相似的功能,通过参数确定该模块完成哪一个功能。例如,在一个模块中包含了以下三个函数:

void add(int a, int b) {
  return a + b;
}

void subtract(int a, int b) {
  return a - b;
}

void multiply(int a, int b) {
  return a * b;
}

偶然内聚

(巧合内聚):完成一组没有关系或松散关系的任务。

模块的各成分之间没有关联,只是把分散的功能合并在一起。这就像是一个工具箱,里面既有锤子也有螺丝刀和胶水,它们之间没有明显的关联

   例:A模块中有三条语句(一条赋值,一条求和,一条传参),表面上看不出任何联系,但是B、C模块中都用到了这三条语句,于是将这三条语句合并成了模块A。模块A中就是偶然内聚。

耦合

2684a9a9cbde4b849bed24a2dc2fcb67.png
1.内容耦合


   一个模块直接修改或操作另一个模块的数据,或者直接转入另一个模块。

   例:模块A中定义了变量a,在模块B中直接使用了。这种情况下模块A和模块B就是内容耦合。

// 模块A直接访问和修改模块B的内部数据。
public class A {
public void methodA() {
B.internalData = 1; // 直接修改B的内部数据
}
}
public class B {
public static int internalData;
}

2.公共耦合


   两个以上的模块共同引用一个全局数据项。

   例:定义了一个全局变量a,在A、B、C模块中均调用了a,这种情况下模块A、模块B、模块C就是公共耦合。

// 模块A和模块B都使用同一个全局数据结构。
public class SharedData {
public static int data;
}
public class A {
public void methodA() {
SharedData.data = 1;
}
}
public class B {
public void methodB() {
int useData = SharedData.data;
}
}

3.外部耦合


 模块与软件的外部设备相连,共享外部资源

4.控制耦合


   一个模块在界面上传递一个信号控制另一个模块,接收信号的模块的动作根据信号值进行调整。一个模块传递信息给另一个模块,用来控制后者的逻辑流程(如决定分支选择)。一个函数接收一个布尔参数,该参数决定函数是计算最大值还是最小值。

   例:模块A获取用户类型(普通用户、高级用户)传递给模块B,模块B根据不同类型的用户提供不同的服务。这种情况下模块A和模块B就是控制耦合。

// 模块A调用模块B,并通过参数控制B的逻辑流程。
public class A {
public void methodA() {
B.methodB(true);
}
}
public class B {
public static void methodB(boolean flag) {
if (flag) {
// 逻辑1
} else {
// 逻辑2
}
}
}

5.标记耦合


   模块间通过参数传递复杂的内部数据结构。模块之间共享一个复合数据结构(如结构体或类),但可能只用到了数据结构中的部分数据。一个函数接收一个员工对象,但只用到了员工的姓名和部门信息,而不是整个员工对象的所有数据。

   例:模块A向模块B传递Object类型的数据。这种情况下模块A和模块B就是标记耦合。

// 模块A调用模块B,传递对象,但B只用对象的一部分。
public class Data {
int part1;
int part2;
}
public class A {
public void methodA() {
Data data = new Data();
B.methodB(data);
}
}
public class B {
public static void methodB(Data data) {
// 只使用data的part1
}
}

 

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());
    }
}

6.数据耦合


   两个模块之间通过参数(通常是必要的)进行交互,传递的是基础数据类型,而不是复杂的对象。一个模块计算税后收入,它接收一个表示税前收入的数字,然后返回税后收入的计算结果。
   例:模块A实现两个数的加法操作,模块B实现两个加数的初始化,模块B将两个加数传给模块A,模块A进行相加。这种情况下模块A和模块B就是数据耦合。耦合度最低,推荐使用。例如:某企业管理信息系统中,采购子系统根据材料价格、数量等信息计算采购的金额,并给财务子系统传递采购金额、收款方和采购日期等信息,则这两个子系统之间的耦合类型为数据耦合

// 模块A调用模块B,通过参数传递数据。
public class A {
public void methodA() {
B.methodB(1);
}
}
public class B {
public static void methodB(int data) {
// 使用数据
}
}

9eccdd0a984f473ea8ebc0dd3409afe1.png 

7.非直接耦合


   模块间没有信息传递。

   例:模块A实现输出字符串,模块B实现接收int数据,两者之间没有信息传递。这种情况下模块A和模块B就是非直接耦合。

如何做到高内聚低耦合

提高内聚
1、确定模块需要的功能点

2、不提供除本职工作外的功能

3、满足可读性、可扩展性、可复用、可维护性要求

4、单独方法不要太长

降低耦合
1、少使用类的继承,多用接口隐藏实现的细节。 java面向对象编程引入接口除了支持多态外, 隐藏实现细节也是其中一个目的。

2、模块的功能化分尽可能的单一,道理也很简单,功能单一的模块供其它模块调用的机会就少。

3、遵循一个定义只在一个地方出现。

4、少使用全局变量。  

5、类属性和方法的声明少用public,多用private关键字,  

6、多用设计模式,比如采用MVC的设计模式就可以降低界面与业务逻辑的耦合度。

7、尽量不用“硬编码”的方式写程序,同时也尽量避免直接用SQL语句操作数据库。

8、最后当然就是避免直接操作或调用其它模块或类(内容耦合);如果模块间必须存在耦合,原则上尽量使用数据耦合,少用控制耦合,限制公共耦合的范围避免使用内容耦合。

例题

下列哪种内聚类型表示模块中的操作必须按照特定顺序执行?
A. 逻辑内聚
B. 时间内聚
C. 过程内聚
D. 通信内聚

功能内聚指的是什么?
A. 模块内的操作可以独立执行
B. 模块内的所有元素共同完成一个单一的任务
C. 模块包含了逻辑上相似的操作
D. 模块的操作处理同一数据

偶然内聚的模块中的元素之间的关系是怎样的?
A. 严格按顺序执行的
B. 逻辑上相似的
C. 没有任何明显的关系
D. 基于时间执行的

通信内聚是指模块中的操作怎样组织?
A. 按照执行时间组织
B. 操作同一份数据或资源
C. 基于操作的逻辑相似性
D. 完全是偶然组织的

顺序内聚与过程内聚有什么不同?
A. 顺序内聚强调操作的逻辑相似性
B. 过程内聚的操作之间没有顺序关系
C. 顺序内聚的输出作为另一操作的输入
D. 过程内聚涉及到数据处理的多个步骤

时间内聚通常在什么情况下出现?
A. 当模块执行与时间无关的任务时
B. 当模块的操作在程序的同一生命周期阶段执行
C. 当模块包含不相关的操作时
D. 当模块的所有操作都完成一个单一任务时

答案解析:

C. 过程内聚
B. 模块内的所有元素共同完成一个单一的任务
C. 没有任何明显的关系
B. 操作同一份数据或资源
C. 顺序内聚的输出作为另一操作的输入
B. 当模块的操作在程序的同一生命周期阶段执行

 例题
1.题目
1.下图中的程序由A、B、C、D、E5个模块组成,下表中描述了这些模块之间的接口,
每一个接口有一个编号。此外,模块A、D和E都要引用一个专用数据区。
那么A和E之间耦合关系是()。
A.公共耦合  B.数据耦合  C.内容耦合  D.无耦合

67e30d0518d946ecb4cfd10b1602b716.png


解析
由于模块A和模块E都引用了专用数据区的内容,所以是公共耦合。
公共耦合指通过一个公共数据环境相互作用的那些模块间的耦合。

例题2
1.题目
2.如下图所示,模块A和模块B都访问相同的全局变量和数据结构,则这两个模块之间的耦合类型为( )耦合。
A.公共
B.控制
C.标记
D.数据
534f271a97424bad8e69c1a909da3407.png

 


解析
公共耦合指通过一个公共数据环境相互作用的那些模块间的耦合,即全局变量和数据结构相互作用模块A和模块B。

例题3

模块A将学生信息,即学生姓名、学号、手机号等放到一个结构体中,传递给模块B。模块A和B之间的耦合类型为(B)耦合。
A.数据
B.标记
C.控制
D.内容

解析
1.数据耦合:两个模块彼此间通过数据参数交换信息。
2.标记耦合:一组模块通过参数表传递记录信息,这个记录是某一个数据结构的子结构,而不是简单变量。
3.控制耦合:两个模块彼此间传递的信息中有控制信息。
4.内容耦合:一个模块需要涉及另一个模块的内部信息。

例题4
题目
4.已知模块A给模块B传递数据结构X,则这两个模块的合类型为(D)。
A.数据耦合
B.公共耦合
C.外部耦合
D.标记耦合

解析
1.数据耦合:一个模块访问另一个模块时,彼此之间是通过简单数据参数(不是控制参数、
公共数据结构或外部变量)来交换输入、输出信息的。
2.公共耦合:若一组模块都访问同一个公共数据环境,则它们之间的耦合就称为公共耦合。
公共的数据环境可以是全局数据结构、共享的通信区、内存的公共覆盖区等。
3.外部耦合:一组模块都访问同一全局简单变量而不是同一全局数据结构,而且不是通过
参数表传递该全局变量的信息,则称之为外部耦合。
4.标记耦合 :一组模块通过参数表传递记录信息,就是标记耦合。这个记录是某一数据结
构的子结构,而不是简单变量。本题描述的是标记耦合。

下列哪种耦合方式的耦合度最低?
A. 数据耦合
B. 控制耦合
C. 外部耦合
D. 内容耦合

如果两个模块通过参数传递基本数据类型进行通信,这种耦合类型是?
A. 无直接耦合
B. 数据耦合
C. 标记耦合
D. 控制耦合

当模块之间共享全局变量时,这种耦合被称为?
A. 外部耦合
B. 公共耦合
C. 控制耦合
D. 内容耦合

模块A调用模块B,并通过对象传递数据,但模块B只使用了传递对象的一部分数据,这种耦合类型是?
A. 数据耦合
B. 标记耦合
C. 控制耦合
D. 内容耦合

下列哪种耦合方式耦合度最高?
A. 数据耦合
B. 标记耦合
C. 外部耦合
D. 内容耦合

当一个模块直接访问另一个模块的内部数据时,这种耦合被称为?
A. 外部耦合
B. 公共耦合
C. 控制耦合
D. 内容耦合

答案及解析:

A. 数据耦合。因为数据耦合只涉及基本数据类型的传递,是耦合度最低的形式

2. B. 数据耦合。这是数据耦合的典型例子,通过基本数据类型进行模块间通信。
3. A. 外部耦合。共享全局变量属于外部耦合的范畴。
4. B. 标记耦合。因为虽然通过对象传递数据,但只使用了对象的一部分。
5. D. 内容耦合。内容耦合是耦合度最高的形式,因为一个模块直接依赖另一个模块的内部结构或数据。
6. D. 内容耦合。直接访问或修改另一个模块的内部数据是内容耦合的特征。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

薛定谔的猫1981

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值