什么是高凝聚力,如何使用/制造?


84

我正在学习计算机编程,并且在多个地方偶然发现了凝聚力的概念,并且我知道对于软件来说,具有“高度凝聚力”是可取的,但这意味着什么?我是一名Java,C和Python程序员,从《 C ++ Primer》一书中学习C ++,该书提到凝聚力而没有将其纳入索引中,您能指出我一些与此主题相关的链接吗?我没有找到有关计算机科学内聚性的Wikipedia页面,因为它只是说这是定性的度量,并且没有给出真实的代码示例。



3
高凝聚力:将相关行为放在一起,将无关行为放到其他位置。
Teoman shipahi 2015年

Answers:


240

高凝聚力是指您的班级做得很好。内聚力低是指班级做了很多没有太多共同点的工作。

让我们来看这个例子:

您有一个将两个数字相加的类,但是同一类将创建一个显示结果的窗口。这是一个低内聚的类,因为窗口和加法操作没有太多共同之处。窗口是程序的可视部分,添加功能是其背后的逻辑。

要创建高凝聚力的解决方案,您必须创建一个Window类和一个Sum类。窗口将调用Sum的方法来获取结果并显示。这样,您将分别开发应用程序的逻辑和GUI。


14
老实说,我没有这样定义凝聚力。您的定义是SRP(单一责任原则)。凝聚力是关于封装方式,相同功能轨道的类是否彼此接近。
v.oddou 2015年

4
SRP(单一责任原则)只是表达相同概念的另一种方式。凝聚力更像是一个度量标准;SRP是一个务实的指导方针,如果遵循该指导方针将导致凝聚力课。
ComDubh

3
我认为类窗口不应调用类总和的任何对象,因为它的唯一目的是使它不需要知道类总和或任何其他类的“存在”。应该有另一个类表示驱动程序,该驱动程序应从中获取结果sum类,并将结果传递给Window类进行渲染
Eklavyaa

1
我不明白 根据鲍伯叔叔的干净守则,这并不是凝聚力。这是SRP
karlihnos

很整洁的解释!感谢
Vincent Llauderes

55

Steve McConnell的《 Code Complete》对此有一个解释:

内聚性是指类中的所有例程或例程中的所有代码在多大程度上支持中心目的。包含 高度相关功能的类被描述为具有很强的内聚力,启发式目标是使内聚力尽可能强。内聚是用于管理复杂性的有用工具,因为类中的代码越多,就越能支撑中心目的,那么大脑就更容易记住代码所做的一切。

通过Bob叔叔的Clean Code实现它的某种方式:

类应具有少量实例变量。类的每个方法都应操纵这些变量中的一个或多个。通常,方法操纵的变量越多,该方法的凝聚力就越大对其类别的。每个方法使用每个变量的类具有最大的内聚性。

通常,创建此类最大内聚的类既不建议也不可行。另一方面,我们希望凝聚力很高。当内聚性很高时,这意味着该类的方法和变量是相互依赖的,并且作为逻辑整体挂在一起。

内聚的概念与耦合的概念紧密相关。此外,还有一个基于高凝聚力启发式的原理,称为“单一责任原理”(SOLID中的S)。


1
我认为您的答案比公认的答案更完整,因为它描述了SRP和凝聚力,因此有助于理解这两个概念的特殊性和差异。
曾经的乐子

18

高凝聚力是一种软件工程概念。基本上,它说一个类应该只做应该做的事情,并且要完全做到。不要用不应执行的功能重载它,并且与它直接相关的任何东西也不应出现在某些其他类的代码中。

例子是很主观的,因为我们还必须考虑规模。一个简单的程序不应过于模块化,否则会变得零碎;而复杂的程序可能需要更高级别的抽象来处理复杂性。

例如电子邮件类别。它应包含来自cc,密件抄送,主题,正文的数据成员,并且可以包含以下方法saveAsDraft(),send(),discardDraft()。但是login()不应在此处,因为有许多电子邮件协议,应该分别实现。


这是SRP的定义:“一个类应该只做应该做的事情,并且要完全做到”。
AliN11

11

内聚性通常使用LCOM(缺乏内聚性)度量标准之一进行度量,原始LCOM度量标准来自Chidamber和Kemerer。参见例如: http //www.computing.dcu.ie/~renaat/ca421/LCOM.html

一个更具体的例子:如果一个类有一个私有字段和三种方法,例如:当所有三个方法都使用该字段执行操作时,该类具有很强的凝聚力。

内聚类的伪代码:

class FooBar {
  private SomeObject _bla = new SomeObject();

  public void FirstMethod() {
    _bla.FirstCall();
  }

  public void SecondMethod() {
    _bla.SecondCall();
  }

  public void ThirdMethod() {
    _bla.ThirdCall();
  }
}

例如,如果一个类具有三个私有字段和三个方法;当所有三种方法仅使用三个字段之一时,则该类的内聚性很差。

内聚性差的类的伪代码:

class FooBar {
  private SomeObject _bla = new SomeObject();
  private SomeObject _foo = new SomeObject();
  private SomeObject _bar = new SomeObject();

  public void FirstMethod() {
    _bla.Call();
  }

  public void SecondMethod() {
    _foo.Call();
  }

  public void ThirdMethod() {
    _bar.Call();
  }
}

做一件事情原则的班级是罗伯特·C·马丁(Robert C. Martin )提出的“单一责任原则”,是SOLID之一原则之一。该原则规定,一个班级只有一个改变的理由。

遵守“单一责任原则”可能会导致更具凝聚力的代码,但我认为这是两回事。


4

这是低凝聚力的一个例子:

class Calculator
{


     public static void main(String args[])
     {

          //calculating sum here
          result = a + b;
          //calculating difference here
          result = a - b;
          //same for multiplication and division
     }
}

但是高内聚性意味着类中的函数可以执行它们应做的事情(就像它们被命名一样)。并且不是某些功能完成其他功能。因此,以下可能是高凝聚力的一个示例:

class Calculator
{


     public static void main(String args[])
     {

          Calculator myObj = new Calculator();
          System.out.println(myObj.SumOfTwoNumbers(5,7));
      }


     public int SumOfTwoNumbers(int a, int b)
     {

          return (a+b);
     }

     //similarly for other operations

}

3

考虑内聚原理的一种通用方法是,您应该将一个代码以及其他依赖于该代码或依赖于它的代码一起定位。凝聚力可以​​而且应该应用于高于班级水平的写作水平。例如,一个包或名称空间在理想情况下应包含与某个通用主题相关的类,并且与其他包/名称空间的依赖程度更高,它们之间的相互依赖性更大。即保持依赖本地。


1

内聚意味着类或方法仅执行一项已定义的工作。方法或类的名称也应该是不言自明的。例如,如果您编写一个计算器,则应将其命名为“ calculator”,而不是“ asdfghj”。您也应该考虑为每个任务创建一个方法,例如减去()add()等...将来可能使用您的程序的程序员确切地知道您的方法在做什么。良好的命名可以减少评论工作

还有一个原则是干-不要重复自己


1

凝聚力一词最初用于描述源代码模块,以定性度量模块源代码之间的关联程度。凝聚力的概念被用于许多领域。例如,一群人,例如一个军事单位,可能具有凝聚力,这意味着该单位中的人们为实现共同目标而共同努力。

源代码凝聚力的实质是模块中的源代码可以共同实现一个共同的,明确定义的目标。创建模块输出所需的最少源代码在模块中,仅此而已。接口定义明确,输入通过接口流入,输出通过接口流出。没有副作用,并且强调简约。

功能上具有凝聚力的模块的好处是,开发和自动化单元测试非常简单。实际上,衡量模块凝聚力的一个好方法是为模块创建全套详尽的单元测试是多么容易。

模块可以是面向对象语言的类,也可以是功能语言或非对象语言(例如C)中的函数。在此领域,衡量内聚力的许多原始工作主要涉及IBM的COBOL程序的工作。 1970年代,凝聚力绝对不只是一个面向对象的概念。

凝聚力概念和关联的耦合概念所来自的研究的初衷是研究易于理解,维护和扩展的程序的特征。目的是能够学习编程的最佳实践,整理这些最佳实践,然后将这些实践教给其他程序员。

优秀的程序员的目标是,在给定环境和要解决的问题的前提下,编写具有尽可能高的内聚力的源代码。这意味着在大型应用程序中,源代码主体的某些部分与该模块或类中源代码的内聚程度有关。由于您要解决的问题,有时可能会获得最好的时间一致性或顺序一致性。

凝聚力的最佳水平是功能凝聚力。具有功能内聚力的模块与数学功能相似,因为您提供了一组输入并获得了特定的输出。真正功能正常的模块除了输出之外不会有副作用,也不会保持任何状态。取而代之的是,它将具有定义良好的接口,该接口封装模块的功能,而不会暴露模块的任何内部结构,并且使用该模块的人员将提供一组特定的输入并获得特定的输出。一个真正的功能模块也应该是线程安全的。

许多编程语言库都包含许多功能模块的示例,无论是类,模板还是函数。最具功能性的内聚示例将是数学函数,例如sin,余弦,平方根等。

其他功能可能会产生副作用或保持某种状态,从而导致这些功能的使用更加复杂。

例如,抛出异常或设置全局错误变量(errno在C中)或必须在序列中使用的函数(strtok()函数(该函数是标准C库中的一个示例,因为它维护内部状态)或提供了一个指针,该指针随后必须被管理或向某些日志实用程序发送日志,这些都是不再具有功能凝聚力的功能的示例。

我读过Yourdon和Constantine的原始书《结构化程序设计》,在那儿我第一次遇到了凝聚力的想法,而Meilir Page-Jones的书《结构化系统设计实用指南》,Page-Jones在描述方面做得更好。耦合和内聚。尤顿和君士坦丁的书似乎更具学术性。史蒂夫·麦康奈尔(Steve McConnell)的书《代码完整》(Code Complete)非常好且实用,修订版中有很多关于良好编程实践的说法。


您说The minimum amount of source code needed to create the module outputs is in the module and no more这与凝聚力无关,而是与DTSTTCPW相关
v.oddou

@ v.oddou,最少的代码确实与凝聚力有关。模块中与模块输出无关的代码越多,代码产生副作用的可能性就越大,因此内聚性越低。每个概念都有不同的观点,尤其是诸如凝聚力之类的概念,这些观点有些含糊。内聚性的测量是定性的,需要一种模糊逻辑,以使用某种类型的标题将特定模块分配给一个类别或另一个类别。仅对输出说最少的代码不足以实现高内聚性,仅仅是其中的几个。
理查德·钱伯斯

1

大多数答案都没有解释什么是内聚力,在鲍伯叔叔的书干净代码中有很好的定义。

类应具有少量实例变量。类的每个方法都应操纵这些变量中的一个或多个。通常,方法操纵的变量越多,该方法对其类的凝聚力就越大。每个方法使用每个变量的类具有最大的内聚性。通常,创建此类最大内聚的类既不建议也不可行。另一方面,我们希望凝聚力很高。当内聚性很高时,这意味着该类的方法和变量是相互依赖的,并且作为逻辑整体挂在一起。

让我用一个类定义来解释

class FooBar {
private _bla;
private _foo;
private _bar;

function doStuff()

   if(this._bla>10){
     this._foo = 10;
     this._bar = 20;
   }

}
function doOtherStuff(){

    if(this._foo==10){
       this._bar = 100;
       this._bla = 200;
    }
}

}

如果您看到上面的示例,则类是内聚的,这意味着变量在类之间共享以一起工作,更多的变量被共享,这意味着该类是高度内聚的并且可以作为一个单元工作。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.