耦合描述的是 两个模块 之间关系的复杂程度。耦合性越低越好,耦合度越高,模块划分越差,越不利于软件变更和复用。根据耦合性的高低依次分为以下六种耦合:
名称 | 概述 | 判断依据 | 修改意见 |
---|---|---|---|
内容耦合 | 一个模块直接修改或者依赖于另一个模块的内容 | 含有 goto/alter 语句模块A 修改了 模块B 传来的数据修改属性没有通过 get/set 方法访问集合变量没有使用迭代器 | 去掉不该有的语句/加上缺少的变量或方法,更改代码保持语义不变。 |
公共耦合 | 模块之间共享全局的数据。共享的是变量 | 两个或多个模块共享全局变量 | 将需要的全局共享变量变成方法参数传入 |
重复耦合 | 模块之间有同样逻辑的重复代码 | 一段业务逻辑在两个地方被调用却没有写在一个方法内,即明显两个地方的代码有极高的重复。 | 创建一个方法,把重复出现的业务逻辑写进方法,通过调用方法传入不同的属性值实现和原来一样的业务逻辑 |
控制耦合 | 一个模块给另一个模块传递控制信息。共享的是逻辑。 | 传递的信息既有数据,还有逻辑(如果只传递数据则为数据耦合),两个模块都需要知道所传递的信息的逻辑含有 swith-case 、 if-else-if-else 、 command 、type 、flag 语句的,比较容易是控制耦合。 | |
印记耦合 | 共享一个数据类型,但是却只用了其中一部分。共享的是数据结构。 | 所传及所需。 | |
数据耦合 | 两个模块的所有参数是同类型的数据项。共享的是数据。 | 所有数据全被用到 使用所传引用对象的方法 一个模块给另一个模块传入的参数得到输出 |
内聚表达的是 一个模块 内部的联系的紧密性。内聚性越高越好,越低越不易实现变更和复用。内聚性由低到高依次分为以下七种内聚:
名称 | 概述 | 判断依据 | 修改意见 |
---|---|---|---|
偶然内聚 | 模块执行多个完全不相关的操作 | 将不相关的操作单独封装 | |
逻辑内聚 | 模块执行一系列相关操作,每个操作的调用由其他模块来决定 | 这些操作逻辑上相似但没有直接关联,表现为 并立的 分支选择语句,即 swith-case 、if-else-if-else (多个条件选择语句中最多只会有一个分支执行) | 将并立的操作单独封装 |
时间内聚 | 模块执行一系列与时间有关的操作(相同的时间) 这些操作在同一时间段内发生 | init() 初始化操作c++ 构造函数、析构函数 | 将每个初始化模块单独封装 |
过程内聚 | 模块执行一些与步骤顺序有关的操作 | 这些操作是解决同一个问题的不同步骤,表现为这些操作的顺序不能颠倒 | |
通信内聚 | 模块执行一系列与步骤有关的操作,并且这些操作在相同的数据上进行。(相同的数据) | 注意是同一数据,有两种类型 - 方法间通信内聚(在多个方法中共享对象,这里,共享不当容易形成公共耦合) - 方法内通信内聚(在方法中多次调用形参的不同属性) | |
功能内聚 | 模块只执行一个操作或达到一个单一目的。(完全以功能(行为)为依据进行模块划分,通常是函数与过程) | 单一的目的 即指一个类内的各属性是否体现一个职责或者各属性之间可否抽象 | |
信息内聚 | 模块进行许多操作,各个都有各自的入口点,每个操作的代码相对独立,而且所有操作都在相同的数据结构上完成。(以数据与功能间的相互支撑为依据进行模块分解) | 接口或抽象类通常是信息内聚的。 |
下面的 gcd
方法内部的代码是哪种类型的内聚?
int gcd(int p, int q) {int r;while(p != 0) {int r = p;p = q % p;q = r;}
}
按照最大公约数的计算方式,完成 p
与 q
的转换,模块达到了一个单一的目的,是功能内聚。
功能内聚强调目标与需求相对应。
下面的 validate_checkout_request
方法的内部代码是哪种类型的内聚?
void validate_checkout_request(input_form i) {if(!(i.name.size()>4 && i.name.size()<20)) {error_message("Invalid name");}if(!(i.date.month>=1 && i.date.month<=12)) {error_message("Invalid month");}
}
分析一:两个 if
判断可能都会为 true
并执行其后语句(非并立),即这两个操作是相关联的,故不是逻辑内聚。在方法中多次调用了形参 i
的不同属性,模块执行的操作是在相同的数据上进行的,故为方法内通信内聚。综上,本题是通信内聚。
分析二:本方法旨在判断输入值是否有效,模块达到一个单一的目的。所以如果需求与方法实现的目标一致(但是本题没有给出需求),也可以是功能内聚。
下面的 validate_checkout_request
方法的内部代码是哪种类型的内聚?validate_checkout_request
方法与 valid_month
方法之间是哪种类型的耦合?
void validate_checkout_request(input_form i) {if(!valid_string(i.name)) {error_message("Invalid name");}if(!valid_month(i)) {error_message("Invalid month");}int valid_month(input_form i) {return i.date.month>=1 && i.date.month<=12;}
}
validate_checkout_request
方法中两个 if
判断语句非并立分支(即并非毫不相关),故排除逻辑内聚。两个判断语句中使用了形参 i
的不同属性,即模块执行的操作在相同的数据上进行,是通信内聚。date
数据,第二个模块只用了其中的一部分,故为印记标记。(因为 valid_month
没有修改 d
的值,故排除内容耦合。)印记耦合改进后为数据耦合(所有传递的属性全被用到):
void validate_checkout_request(input_form i) {if(!valid_string(i.name)) {error_message("Invalid name");}if(!valid_month(i.date.month)) {error_message("Invalid month");}
}int valid_month(int month) {return month>=1 && month<=12;
}
下面的 validate_checkout_requst
方法与 valid
方法之间是哪种类型的耦合?
void validate_checkout_request(input_form i) {if(!valid(i.name.STRING)) {error_message("Invalid name");}if(!valid(i.date.DATE)) {error_message("Invalid month");}
}int valid(String s, int type) {swith(type) {case STRING:return strlen(s)=1 && d.month<=12;}
}
第一个模块给第二个模块传递的信息既有数据,又有逻辑:第一个模块发送给第二个模块的消息是由一个值 i.name/i.date
和数据类型 STRING/DATE
组成,第二个模块也要根据数据类型来判断值是否有效这个逻辑来解析消息。所以两个模块共享的是逻辑,是控制耦合。
同时,第二个模块对接收到的 date
类型的参数,只用了其中的 month
属性,故也为印记耦合。
控制耦合和印记耦合是可以接受的。但也可以进行代码完善,完善后为数据耦合:
void validate_checkout_request(input_form i) {if(!valid(i.name.STRING)) {error_message("Invalid name");}if(!valid(i.date.DATE)) {error_message("Invalid month");}
}int valid_string(String s) {return strlen(s)return d.month>=1 && d.month<=12;
}
上一篇:vue3基础