强内聚 弱耦合

强内聚 弱耦合

 

 

内聚(Cohesion)是一个模块内部各成分之间相关联程度的度量。耦合(Coupling)是模块之间依赖程度的度量。内聚和耦合是密切相关的,与其它模块存在强耦合的模块通常意味着弱内聚,而强内聚的模块通常意味着与其它模块之间存在弱耦合。模块设计追求强内聚,弱耦合。
一、内聚强度
内聚按强度从低到高有以下几种类型:
(1) 偶然内聚。如果一个模块的各成分之间毫无关系,则称为偶然内聚。
(2) 逻辑内聚。几个逻辑上相关的功能被放在同一模块中,则称为逻辑内聚。如一个模块读取各种不同类型外设的输入。尽管逻辑内聚比偶然内聚合理一些,但逻辑内聚的模块各成分在功能上并无关系,即使局部功能的修改有时也会影响全局,因此这类模块的修改也比较困难。
(3) 时间内聚。如果一个模块完成的功能必须在同一时间内执行(如系统初始化),但这些功能只是因为时间因素关联在一起,则称为时间内聚。
(4) 过程内聚。如果一个模块内部的处理成分是相关的,而且这些处理必须以特定的次序执行,则称为过程内聚。
(5) 通信内聚。如果一个模块的所有成分都操作同一数据集或生成同一数据集,则称为通信内聚。
(6) 顺序内聚。如果一个模块的各个成分和同一个功能密切相关,而且一个成分的输出作为另一个成分的输入,则称为顺序内聚。
(7) 功能内聚。模块的所有成分对于完成单一的功能都是必须的,则称为功能内聚。
二、耦合强度
耦合的强度依赖于以下几个因素:(1)一个模块对另一个模块的调用;(2)一个模块向另一个模块传递的数据量;(3)一个模块施加到另一个模块的控制的多少;(4)模块之间接口的复杂程度。
耦合按从强到弱的顺序可分为以下几种类型:
(1)内容耦合。当一个模块直接修改或操作另一个模块的数据,或者直接转入另一个模块时,就发生了内容耦合。此时,被修改的模块完全依赖于修改它的模块。
(2)公共耦合。两个以上的模块共同引用一个全局数据项就称为公共耦合。
(3)控制耦合。一个模块在界面上传递一个信号(如开关值、标志量等)控制另一个模块,接收信号的模块的动作根据信号值进行调整,称为控制耦合。
(4)标记耦合。模块间通过参数传递复杂的内部数据结构,称为标记耦合。此数据结构的变化将使相关的模块发生变化。
(5)数据耦合。模块间通过参数传递基本类型的数据,称为数据耦合。
(6)非直接耦合。模块间没有信息传递时,属于非直接耦合。
如果模块间必须存在耦合,就尽量使用数据耦合,少用控制耦合,限制公共耦合的范围,坚决避免使用内容耦合。

 

 

举例:

3.1 耦合类型

耦合主要分为以下几类,耦合度从低到高,分别是:

非直接耦合 < 数据耦合 < 标记耦合 < 控制耦合 < 外部耦合 < 公共耦合 < 内容耦合

    非直接耦合(Nondirect coupling)
    A/B两个模块之间没有任何关系,不会调用对方的API,不依赖对方的结构体、宏……不用包含头文件,删除一个模块对另一个模块没有任何影响,它们之间的联系完全是通过主模块的控制和调用来实现的,这种耦合关系称之为 非直接耦合。
    例子:

//A模块
    int Add(int num1,int num2)
    {
        return (num1+num2);
    }

//B模块
    typedef struct _SUB_PARA_
    {
        int num1;
        int num2;
    }SubPara;

    int Sub(SubPara* pNum)
    {
        return (pNum->num1-pNum->num2);
    }

   
    数据耦合(Data Coupling)
    数据耦合指的是,两个模块调用时,传递的是简单的数据值,不是数据结构或者其他复杂变量。
    如果一个模块访问另一个模块时,彼此之间是通过数据参数(不是控制参数、公共数据结构或外部变量)来交换输入、输出信息的,则称这种耦合为数据耦合。。在软件程序结构中至少必须有这类耦合。
    在上例子中,A/B之间没有相互的依赖,但是可能A、B的API都会被C模块调用,(个人意见:我们应该把C模块划分到高一层去)。
    C调用A,用的是参数传入,传递的都是int型的入参,即为C和A为数据耦合。
    例子:

//C模块
    int Sum(int num1 , int num2 ,int num3,int 4)
    {
        int res1 = Add(num1,num2);
        int res2 = Add(num3,num4);
        return Add(res1,res2);
    }

 
    标记耦合(Stamp Coupling)
    如果在调用过程中,传递的不是普通参数,而是一个结构体,这个结构体是属于某个模块的,而不是简单变量。
    如果一组模块通过参数表传递记录信息,就是标记耦合。事实上,这组模块共享了这个记录,它是某一数据结构的子结构,而不是简单变量。这要求这些模块都必须清楚该记录的结构,并按结构要求对此记录进行操作。在设计中应尽量避免这种耦合,它使在数据结构上的操作复杂化了。
    原则上,应该尽量把使用同一结构体的操作尽量集中在一个模块中,消除这种耦合。
    还是这个例子。
    C调用B,用的是结构体传入,即为标记耦合

//C模块第二部分
    int Sub2(int num1, int num2)
    {
        SubPara Temp;
        Temp.num1 = num1;
        Temp.num2 = num2;
        return Sub(&Temp);
    }

   

    控制耦合
    如果一个模块通过传送开关、标志、名字等控制信息,明显地控制选择另一模块的功能,就是控制耦合。
    偷一张别人的图,看的很明白:
    在这里插入图片描述
    不论这个Flag是怎么传过来的,在功能逻辑上,B模块是依赖于A模块的。

    外部耦合(External Coupling)
    一组模块都访问同一全局简单变量而不是同一全局数据结构,而且不是通过参数表传递该全局变量的信息,则称之为外部耦合。
    举个例子,DE模块都直访问了一个全局变量,而不是通过入参传入。

//D模块
    int g_Para = 1;
    
    int Func1()
    {
        return (g_Para+1);
    }

//E模块
    extern int g_Para;
    int Func2()
    {
        return (g_Para+2);
    }
 

    公共耦合
    若一组模块都访问同一个公共数据环境,则他们之间的耦合就称为公共耦合。公共的数据环境可以是全局数据结构、共享的通信区、内存的公共覆盖区等。
    类似外部耦合,只不过依赖不再是简单变量。

    内容耦合
    又称病态耦合。如果发生下列情形,两个模块之间就发生了内容耦合。
        一个模块直接访问另一个模块的内部数据;
        一个模块不通过正常入口转到另一模块内部;
        两个模块有一部分程序代码重叠(只可能出现在汇编语言中);
        一个模块有多个入口。

    在内容耦合的情形,所访问模块的任何变更,或者用不同的编译器对它再编译,都会造成程序出错。好在大多数高级程序设计语言已经设计成不允许出现内容耦合。它一般出现在汇编语言程序中。这种耦合是模块独立性最弱的耦合。

3.2 如何解耦

依赖注入、命名查找
4. 总结

我的观点是:
高内聚,低耦合肯定是我们的追求,但并不是内聚越高,耦合越低就一定好,并非要是使用很多兜圈子的代码来降低耦合,我们应该允许单向调用。

    项目设计应该先做分层设计。
    同层级的模块间,尽量保持相互独立,不相互调用。如果无法避免,尽量把被高度依赖的部分封装到同一个模块中,这个模块作为必选功能模块,不可移除
    上层可以调用下层模块的API和结构体。调用多使用传参,传参多用普通类型
    每个模块维护自己的内部变量和状态,如果变量少就用基本数据类型,多就用结构体,与外界任何交互都有标准的输入输出接口。
————————————————
版权声明:本文为CSDN博主「taotao830」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/tao475824827/article/details/104157419/

引用地址:https://www.cnblogs.com/gaoxue/archive/2011/12/27/2303699.html

posted @ 2023-05-28 00:25  桃花雪  阅读(169)  评论(0编辑  收藏  举报