基本信息
耦合性(或称"耦合度")
英文 : coupling
耦合性也叫块间联系。指软件系统结构中各模块间相互联系紧密程度的一种度量。模块之间联系越紧密,其耦合性就越强,模块之间越独立则越差,模块间耦合的高低取决于模块间接口的复杂性,调用的方式以及传递的信息。
形象的说,就是要将代码写的和电脑一样,主类就是电脑的主机箱,当程序需要实现什么功能的时候只需要在其他的类引入接口,就像电脑上的usb接口。
非直接耦合(NondirectCoupling)
如果两个模块之间没有直接关系,它们之间的联系完全是通过主模块的控制和调用来实现的,这就是非直接耦合。这种耦合的模块独立性最强。
1) 非直接耦合:
#include void
main()
{ inta,b; printf("printaninteger number:");
scanf("%d",&a); if(a<0) printf("dataerror!");
else { b=fac(a); prt(b); } }
int fac(int n) {
int f; if(n==0, n==1) f=1; else f=fac(n-1)*n; return(f); }
void prt(int x) { printf("%d\n",x); }
/*函数 fac 和 prt 之间为非直接耦合关系*/
数据耦合(DataCoupling)
如果一个模块访问另一个模块时,彼此之间是通过数据参数(不是控制参数、公共数据结构或外部变量)来交换输入、输出信息的,则称这种耦合为数据耦合。由于限制了只通过参数表传递数据,按数据耦合开发的程序界面简单、安全可靠。因此,数据耦合是松散的耦合,模块之间的独立性比较强。在软件程序结构中至少必须有这类耦合。
数据耦合的例子:
/** 数据耦合 * 主函数main()和Multiply(int x, int y)之间为数据耦合关系*/
#include
int Multiply(int x, int y)
{ return(x * y); }
void main() {
int x = 0;
int y = 0;
scanf("%d%d", &x, &y);
printf("x * y = %d\n", Multiply(x,y));
}
印记耦合(StampCoupling)
如果一组模块通过参数表传递记录信息,就是标记耦合。事实上,这组模块共享了这个记录,它是某一数据结构的子结构,而不是简单变量。这要求这些模块都必须清楚该记录的结构,并按结构要求对此记录进行操作。在设计中应尽量避免这种耦合,它使在数据结构上的操作复杂化了。如果采取"信息隐蔽"的方法,把在数据结构上的操作全部集中。
#include voidmain() {intj,b[10]; for(j=0; j<10;j++) b[j]=j; add3(b[10]); } voidadd3(inta[10]) { int s=0; s=a[3]+1; printf("%d\n",s); } /*主函数 main 和子函数 sum 之间为特征耦合关系*/
控制耦合(ControlCoupling)
如果一个模块通过传送开关、标志、名字等控制信息,明显地控制选择另一模块的功能,就是控制耦合。这种耦合的实质是在单一接口上选择多功能模块中的某项功能。因此,对所控制模块的任何修改,都会影响控制模块。另外,控制耦合也意味着控制模块必须知道所控制模块内部的一些逻辑关系,这些都会降低模块的独立性。
#include
static bool Signal;
void AdultOrNot(int age)
{ if (age > 18) { Signal = 1; }
else { Signal = 0; } }
void WineOrNot() {
if (Signal == 1) {
printf("%s\n", "您已到达法定饮酒年龄!"); }
else{
printf("%s\n","您未到达法定饮酒年龄!");
}}int main() { int Age = 0; printf("%s","请输入您的年龄:"); scanf("%d", &Age);
AdultOrNot(Age); WineOrNot();
}
外部耦合(ExternalCoupling)
一组模块都访问同一全局简单变量而不是同一全局数据结构,而且不是通过参数表传递该全局变量的信息,则称之为外部耦合。例如C语言程序中各个模块都访问被说明为extern类型的外部变量。外部耦合引起的问题类似于公共耦合,区别在于在外部耦合中不存在依赖于一个数据结构内部各项的物理安排。
#include inta,b; voidmain() { pin(); b=a+1; prt(); } voidpin() { printf("printaninteger number: "); scanf("%d",&a); } voidprt() { printf("%d\n",b); } /*三个函数之间都存在外部耦合关系*/
公共耦合(CommonCoupling)
若一组模块都访问同一个公共数据环境,则它们之间的耦合就称为公共耦合。公共的数据环境可以是全局数据结构、共享的通信区、内存的公共覆盖区等。 这种耦合会引起下列问题:
所有公共耦合模块都与某一个公共数据环境内部各项的物理安排有关,若修改某个数据的大小,将会影响到所有的模块。
无法控制各个模块对公共数据的存取,严重影响软件模块的可靠性和适应性。
公共数据名的使用,明显降低了程序的可读性。
公共耦合的复杂程度随耦合模块的个数增加而显着增加。若只是两个模块之间有公共数据环境,则公共耦合有两种情况。
若一个模块只是往公共数据环境里传送数据,而另一个模块只是从公共数据环境中取数据,则这种公共耦合叫做松散公共耦合。若两个模块都从公共数据环境中取数据,又都向公共数据环境里送数据,则这种公共耦合叫做紧密公共耦合。只有在模块之间共享的数据很多,且通过参数表传递不方便时,才使用公共耦合。否则,还是使用模块独立性比较高的数据耦合好些。
内容耦合(ContentCoupling)
如果发生下列情形,两个模块之间就发生了内容耦合。
一个模块直接访问另一个模块的内部数据;
一个模块不通过正常入口转到另一模块内部;
两个模块有一部分程序代码重叠(只可能出现在汇编语言中);
一个模块有多个入口。
在内容耦合的情形,所访问模块的任何变更,或者用不同的编译器对它再编译,
都会造成程序出错。好在大多数高级程序设计语言已经设计成不允许出现内容
耦合。它一般出现在汇编语言程序中。这种耦合是模块独立性最弱的耦合。
降低耦合度的方法
1、少使用类的继承,多用接口隐藏实现的细节。 java面向对象编程引入接口除了支持多态外, 隐藏实现细节也是其中一个目的。
2、模块的功能化分尽可能的单一,道理也很简单,功能单一的模块供其它模块调用的机会就少。(其实这是高内聚的一种说法,高内聚低
耦合一般同时出现,为了限制篇幅,我们将在以后的版期中讨论)。
3、遵循一个定义只在一个地方出现。
4、少使用全局变量。
5、类属性和方法的声明少用public,多用private关键字,
6、多用设计模式,比如采用MVC的设计模式就可以降低界面与业务逻辑的耦合度。
7、尽量不用“硬编码”的方式写程序,同时也尽量避免直接用SQL语句操作数据库。
8、最后当然就是避免直接操作或调用其它模块或类(内容耦合);如果模块间必须存在耦合,原则上尽量使用数据耦合,少用控制耦合,
限制公共耦合的范围,避免使用内容耦合。
1) 偶然类聚: Word 窗口的工具菜单,在本菜单中,各工具间基本没什么联系。该菜单具有偶然内聚。
2) 逻辑内聚: 一个函数能打印季度开支报告、月份开支报告和日开支报告,具体打印哪一个,将由传入的 控制标志决定,该函数具有逻辑内聚性。
3) 时间内聚: 操作系统的开机初始化模块,包含的动作没什么大的关系,但必须在开机后的一段时间内都 完成。整个开机初始化模块具有时间内聚。
4) 过程内聚: 一个模块,为某员工计算工龄工资:打开员工信息文件取出员工记录;按一定的算法计算工 龄;按一定的算法计算工龄工资。 该模块中的三个相对独立的子功能必须以特定次序执行,整个模块具有过程内聚。
5) 通信内聚:一个模块,接收一个远程传来的信息文件,保存,同时马上打印该文件。 该模块针对同一个文件操作,打印和存盘操作没有前后顺序关系,整个模块具有通信内聚。
6) 顺序内聚:某干部退休,模块计算他的离/退休工资:打开干部信息文件;读出文件中他 的职务/级别等信息,通过一定算法判断他是否具备离休资格,结论写入文件;再读出文件 中他的目前工资
、工作年限、是否离休等信息,通过一定算法计算他的离/退休工资,再结 果写入文件。 该模块操作同一个文件,必须先判断出他是否离休,再计算离/退休工资。整个模块具有顺 序内聚。
7) 功能内聚: intfac(intn) { int f; if(n==0, n==1) f=1; else f=fac(n-1)*n; return(f); } /*函数 fac,计算 n!,本模块功能单一,具备功能内聚*/