“高凝聚力”是什么意思?


28

我是一名学生,最近加入了一家软件开发公司作为实习生。回到大学时,我的一位教授曾经说过,我们必须努力实现“低耦合和高凝聚力”。

我了解低耦合的含义。这意味着将单独组件的代码分开保存,这样一处更改不会破坏另一处的代码。

但是,高凝聚力是什么意思。如果这意味着将同一组件的各个部分很好地集成在一起,我不知道这将如何变得有利。

高凝聚力是什么意思?可以解释一个例子来理解它的好处吗?



1
维基百科文章是否不能充分回答您的问题? en.wikipedia.org/wiki/Cohesion_(computer_science)–
伊恩·卡洛尔


1
@EoinCarroll:不幸的是,目前维基百科上的文章并没有提供任何新程序员可以使用的良好示例。理论是有好处的,但只有在您完成围绕低内聚力的错误之前,理论才真正成立。高凝聚力是我花了几年时间进行编程才能完全理解它为什么重要以及如何实现的主题之一。
Spoike,2012年

在阅读“干净代码”之前,我从未真正了解过凝聚力。你也应该
塞巴斯蒂安·雷德尔2014年

Answers:


25

从OO角度看凝聚力的一种方法是,如果类中的方法使用任何私有属性。gnat在这里的答案中指出,使用诸如LCOM4(缺乏内聚方法)之类的指标,您可以识别可以重构的类。您想要重构方法或类以使其更具凝聚力的原因是,它使代码设计更易于他人使用。相信我; 解决这些问题时,大多数技术主管和维护程序员都会爱上您。

您可以在构建过程中使用诸如Sonar之类的工具来识别代码库中的低内聚性。我可以想到几个非常常见的情况,即哪些方法的“内聚性”较低:

情况1:方法与类完全无关

考虑以下示例:

public class Food {
   private int _foodValue = 10;

   public void Eat() {
     _foodValue -= 1;
   }

   public void Replenish() {
     _foodValue += 1;
   }

   public void Discharge() {
     Console.WriteLine("Nnngghhh!");
   }
}

其中一种方法Discharge()缺乏凝聚力,因为它没有涉及到班上的任何私人成员。在这种情况下,只有一个私人成员:_foodValue。如果它对类的内部没有任何作用,那么它真的属于那里吗?该方法可以移到另一个可以命名为的类FoodDischarger

// Non-cohesive function extracted to another class, which can
// be potentially reused in other contexts
public FoodDischarger {
  public void Discharge() {
    Console.WriteLine("Nnngghhh!");
  }
}

在用Javascript编写代码时,由于函数是一类对象,所以放电可以是自由函数:

function Food() {
    this._foodValue = 10;
}
Food.prototype.eat = function() {
    this._foodValue -= 1;
};
Food.prototype.replenish = function() {
    this._foodValue += 1;
};

// This
Food.prototype.discharge = function() {
    console.log('Nnngghhh!');
};
// can easily be refactored to:
var discharge = function() {
    console.log('Nnngghhh!');
};
// making it easily reusable without creating a class

情况2:实用程序类

这实际上是破坏凝聚力的常见情况。每个人都喜欢实用程序类,但是这些通常都表明设计缺陷,并且大多数时候会使代码库难以维护(因为与实用程序类相关的依赖性很高)。考虑以下类别:

public class Food {
    public int FoodValue { get; set; }
}

public static class FoodHelper {

    public static void EatFood(Food food) {
        food.FoodValue -= 1;
    }

    public static void ReplenishFood(Food food) {
        food.FoodValue += 1;
    }

}

在这里,我们可以看到实用程序类需要访问该类中的属性Food。在这种情况下,实用程序类中的方法根本没有内聚性,因为它需要外部资源来完成其工作。在这种情况下,将方法放在自己正在使用的类中会更好吗(很像第一种情况)?

情况2b:实用程序类中的隐藏对象

实用程序类的另一种情况是存在未实现的域对象。编程字符串操作时,程序员的第一个下意识的反应是为其编写实用程序类。就像这里验证两个常见字符串表示的代码一样:

public static class StringUtils {

  public static bool ValidateZipCode(string zipcode) {
    // validation logic
  }

  public static bool ValidatePhoneNumber(string phoneNumber) {
    // validation logic
  }

}

这里最没有意识到的是邮政编码,电话号码或任何其他字符串表示形式都可以是对象本身:

public class ZipCode {
    private string _zipCode;
    public bool Validates() {
      // validation logic for _zipCode
    }
}

public class PhoneNumber {
    private string _phoneNumber;
    public bool Validates() {
      // validation logic for _phoneNumber
    }
}

@codemonkeyism本博文中详细介绍了不应直接“处理字符串” 的概念,但与内聚紧密相关,因为程序员通过将逻辑放入实用程序类来使用字符串的方式。


现在,只要让我们的ORM正确处理自定义的ZipCode和PhoneNumber类即可:
皮特2014年

+1个好例子。关于最后一点,另请参见sourcemaking.com/refactoring/primitive-obsession
AlexFoxGill 2014年

@Pete如果您提供从字符串到定义类型(ZipCode,PhoneNumber)的转换运算符,则您的ORM将处理该问题:msdn.microsoft.com/en-us/library/85w54y0a.aspx
Marcus

9

高凝聚力意味着将相似和相关的事物保持在一起,以耦合或融合共享内容,功能,原因或目标的部分。换句话说,低内聚性可能意味着例如功能/类/代码实体,该实体服务于多个目的,而不是“ 指向重点 ”。一种随身携带的思想是做一件事并做好。其他可能包括一个明显的事实,即您在许多地方都没有复制类似的功能。这也提高了代码库的局部性,某些种类的东西在某个地方找到(文件,类,函数集...),而不是分散在周围。

例如,考虑一个有两个或三个目的的类:加载/存储资源(例如文件),然后分析并显示内容。这样的类具有较低的内聚性,因为它管理至少两个根本不相关的单独任务(文件I / O,分析和显示)。高凝聚力的设计可以使用不同的类来加载和存储资源,对其进行分析然后进行显示。

另一方面,低耦合的目的是使不同的事物保持分离-从而使它们之间的交互尽可能少,从而降低了复杂性并简化了设计。


7

这意味着给定对象的各个部分与该对象的功能密切相关。这意味着就功能或责任而言,对象内几乎没有浪费。反过来,这可以提高对有关对象应用于什么目的的理解。


它不仅仅适用于对象吗?例如,将与任务相关的objcts /功能分组在名称空间中?
stijn 2012年
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.