回到基础:内聚和耦合第2部分

发布于:2021-01-25 15:46:55

0

289

0

内聚 耦合

这篇文章是我关于内聚和耦合的文章的延续,它是一系列回到基础文章的一部分,这些文章研究和质疑了软件开发的一些核心原则和实践。

在上一篇文章中,我讨论了什么是内聚和耦合,以及它们各自的一些好处。

现在我想看看内聚和解耦在软件系统上的实际应用。

粒度影响内聚和耦合

这是关于内聚解耦的关键误解。

内聚和解耦完全与模块的粒度有关。

从这里开始,我不会说类、软件或系统,当我提到内聚和耦合时,我会说模块。

看,问题是我们需要能够定义什么是模块,以便确定某个东西是松散耦合的还是高度内聚的。

让我给你举个例子。让我们以上面提到的链表类为例。如果我们将模块定义为代码中的任何类,那么它是相当解耦的。但是,如果我们将模块定义为系统中的任何类或基元类型,我们的链表实现会突然依赖于许多其他东西。现在我们在类中声明的变量依赖于数组、字符串类或整数实现。

当我们深入到模块的上述层次时,我们最终会得到更多表示更紧密耦合的依赖关系。

凝聚力呢?如果愿意,请考虑Enterprise FizzBuzz。这是FizzBuzz问题的一个实现:

  • 打印从1到100的数字

  • 如果数字可被3整除,请打印“ Fizz”

  • 如果数字可以被5整除,请打印“ Buzz”

  • 如果数字是可分割的3和5,则打印“ FizzBuzz”

它是这个简单问题的一个实现,使用了3个程序集和16+个类。如果我们认为一个模块是一个类,那么它就没有很强的内聚性,因为一个或两个方法中应该包含的内容的职责分布在16个程序集中。如果我们认为一个模块是一个程序集,那么它仍然没有很强的内聚性。

在这个软件变得内聚之前,我们必须把所有的方法都放大到模块是一个程序级别,但是在这个级别上,它与它所依赖的所有其他框架是非常耦合的。

本节有两个重要的要点:

  1. 我们在查看软件时如何定义模块会影响内聚和耦合。

  2. 我们用来构建软件的粒度会影响内聚和耦合(这一点隐藏在enterprise FizzBuzz示例中。在本例中,代码作者将责任定义为非常小的内容)。

综上所述,我们可以从放大和缩小的不同层次来观察代码,并确定其耦合性和内聚性,我们还可以将软件构建为具有相同效果的不同大小的“乐高积木”。

当内聚力和耦合力成反比时

在这样的背景下,我们终于可以回答这样一个问题:是否有可能实现高内聚和松耦合。答案是“在一定程度上”

如果我们试图过分地推进松散耦合区,我们会发现,我们最终会使我们对责任的定义变得非常非常小,在这一点上,我们失去了凝聚力的质量。

我要再举一次过度使用接口的例子。

考虑一下当我们创建一个接口来“减少耦合”以便创建单元测试时会发生什么,我们最终会降低内聚性,因为我们的类所引用的类(一个跳跃)现在是一个由类(两个跳跃)实现的接口

现在考虑一下当我们添加一个依赖注入模块,甚至仅仅是一个工厂来获得实现时会发生什么,我们曾经简单且高度内聚的实现分布在一个接口上,这个接口是由一个类实现的,我们必须在一个工厂中查找这个类,这个工厂包含某种映射文件来将接口映射到实施。(3-4跳)

我知道这个概念看起来很奇怪,但让我看看能否用一个真实的例子来解释它。

再考虑一下高内聚性棒球。它已经处于0的最佳耦合。它不依赖于其他任何东西。但是,如果我们想,我们可以尝试去耦合它。如何?我们必须放大到一个点,我们可以认为,外部缝合是耦合到套管,这是耦合到内部球。

我们可以把棒球拆开,然后设计一种接口,允许外壳和基板有不同的绑定机制,以防止外壳直接接触内球。

如果我们这样做的话,我们会让球的碎片到处都是,它就不会有很强的凝聚力,甚至功能性。

我们可以用软件来做到这一点,在这一点上,我们已经获得了松散耦合和高内聚的最大质量,我们可以尝试以内聚为代价来推动解耦。

凝聚力更重要

似乎我是在说内聚比解耦更重要,实际上我是。

如果您还记得紧密内聚和松散耦合的优点,您可能已经意识到大多数优点都是相同的,只是紧密内聚给我们带来了增加理解的好处。

我倾向于把软件的理解和简单性放在最重要的事情之上,因为它们对维护和调试最有帮助。

当我们试图增加解耦时,考虑内聚力是非常重要的。它还可以帮助我们非常清楚地衡量何时使解耦最大化。在任何解耦都会损害内聚力的情况下,我们就这样做了。

那么依赖注入是不是不好呢?

不,一点也不。依赖注入和其他解耦方法都有它们的位置,但非常重要的是,我们不要在任何情况下盲目地将它们用于每个类。

当我们考虑使用任何类型的框架或模式来解耦我们的软件时,我们必须意识到我们在内聚性和可理解性方面正在失去什么。

我将在这里再谈一件事,因为我认为这不是很明显。考虑一下消息总线对于将不同的应用程序集成在一起有多好。消息系统可以将需要相互通信的不同应用程序解耦,使它们具有高度的内聚性和非常松散的耦合性。

现在,考虑一下消息总线在应用程序中的糟糕程度,我知道对于解耦应用程序中的事件、命令和其他通信来说,它是一个相当流行的解决方案,但是很多时候,解耦的代价对内聚性和可理解性是一个非常高的打击。

别误会,有时内部消息总线是应用程序的一个很好的解决方案,但在许多情况下,它对您的伤害大于帮助。

都是大小合适的乐高积木

Scott Hanselman经常喜欢谈论“合适大小的乐高积木”,我完全同意他的观点。弄清楚如何在保持内聚性的同时适当地解耦应用程序就是弄清楚模块的理想大小。

有时,答案甚至可能是将应用程序拆分为多个应用程序。