【问题标题】:Difference Between Cohesion and Coupling内聚与耦合之间的区别
【发布时间】:2011-03-06 08:16:01
【问题描述】:

内聚和耦合有什么区别?

耦合和内聚如何导致软件设计的好坏?

有哪些示例概述了两者之间的区别以及它们对整体代码质量的影响?

【问题讨论】:

标签: oop architecture theory software-design ooad


【解决方案1】:

内聚表示模块内的关系,而耦合表示模块之间的关系。增加内聚对软件有好处,但避免增加对软件的耦合。 内聚代表模块的功能强度。耦合表示模块之间的独立性。在凝聚力中,模块专注于单一事物。在耦合中,模块连接到其他模块。因此,在设计时,您应该努力实现高内聚和低耦合。

【讨论】:

    【解决方案2】:

    简单地说,内聚表示代码库的一部分形成逻辑上单一的原子单元的程度。另一方面,耦合表示单个单元依赖于其他单元的程度。换句话说,它是两个或多个单元之间的连接数。数量越少,耦合度越低。

    本质上,高内聚意味着将彼此相关的代码库部分保存在一个地方。同时,低耦合是为了尽可能地分离代码库中不相关的部分。

    从内聚和耦合角度看代码类型:

    理想是遵循准则的代码。它是松散耦合和高度内聚的。我们可以用这张图来说明这样的代码:

    God Object是引入高内聚和高耦合的结果。它是一种反模式,基本上代表一段代码可以一次完成所有工作: 选择不当发生在不同类或模块之间的边界选择不当

    破坏性解耦是最有趣的一种。当程序员试图解耦代码库以至于代码完全失去焦点时,有时会发生这种情况:

    阅读更多here

    【讨论】:

    • 优秀的文章和插图!如果我可以建议对一个想法进行改进,我喜欢“选择不当”如何将具有不相关语义的组件组保持在小群中,但我认为它们之间应该有明显更多的箭头。毕竟,即使在您的 4 方格图上,它也属于“耦合”轴的上限范围。
    • 我还要说,“选择不佳”应该在每个群体内有更少的箭头。使用您文章中的“文件夹结构”示例,您将其归类为“选择不当”的存储库或工厂肯定不会相互交流。
    • 更新:我向图片的原作者and the author agreed with them提出了这些建议。
    • 这是一个很好的答案,简单解释。我发现“选择不当”的最常见形式之一就是您可能所说的“类型凝聚力”。您到处都可以看到这一点,例如一个包含“屏幕”的包结构,所有屏幕都在其中,另一个称为“对话框”,所有对话框都在该包中。结果是您在一个地方获得了 MyABDialog,而在另一个地方获得了 MyABScreen。以这种方式组织实际上破坏了凝聚力,并在不需要存在的地方引入了耦合,而 看起来 你是一个应用良好的模式。
    【解决方案3】:

    理论差异

    凝聚力

    • 凝聚力是模块相对功能强度的指标。
    • 内聚模块执行单个任务,几乎不需要与其他模块进行交互 程序其他部分的组件。
    • 具有高内聚性和低耦合性的模块被认为是功能独立的 的其他模块。

    凝聚力的分类

    1.巧合 2.逻辑 3.时间 4.程序 5.通信 6.顺序 7.功能

    耦合

    • 耦合表示模块之间的相对相互依赖关系。
    • 两个模块之间的耦合程度取决于它们的接口复杂性。

    【讨论】:

      【解决方案4】:

      内聚力指的是单个类的设计方式。内聚是面向对象的原则,它与确保一个类的设计具有单一的、重点突出的目的最密切相关。 一个类越集中,该类的凝聚力就越大。高内聚的优点是这样的类比低内聚的类更容易维护(并且更少更改)。高内聚的另一个好处是目标明确的类往往比其他类更易于重用。

      在上图中,我们可以看到,在低内聚情况下,只有一个类负责执行许多不常见的工作,这降低了可重用性和维护的机会。但是在高内聚中,所有作业都有一个单独的类来执行特定作业,从而提高了可用性和维护性。

      【讨论】:

        【解决方案5】:

        凝聚力这个词在软件设计中的含义确实有点违反直觉。

        凝聚力的共同含义是,能很好地粘合在一起的东西,是统一的,其特点是分子吸引力般的强键。然而,在软件设计中,这意味着争取一个理想情况下只做一件事的类,因此甚至不涉及多个子模块。

        也许我们可以这样想。当一个部分是唯一的部分(只做一件事并且不能进一步分解)时,它具有最大的凝聚力。这是软件设计中所需要的。凝聚力只是“单一责任”或“关注点分离”的另一个名称。

        耦合这个词非常直观,这意味着当一个模块不依赖于太多其他模块并且它所连接的模块可以很容易地替换时,例如服从liskov substitution principle

        【讨论】:

        • 为什么人们一直使用模块这个词而不是类?
        • @northerner 只是更通用的术语。
        • 这里有一篇文章描述了凝聚力和 SRP 实际上是一回事。它进一步解释说,你积极做的唯一一件事——就保持“高度凝聚力”而言——不是将属于一起的事物分开。这与 SRP相同。 stackoverflow.com/questions/11215141/…
        【解决方案6】:

        简单地说,内聚意味着一个类应该代表一个单一的概念。

        如果类的所有特性都与类所代表的概念相关,那么类的公共接口就是内聚的。 例如,没有 CashRegister 类,而是拥有 CashRegister 和 Coin 功能凝聚力使其分为 2 个类 - CashRegister 和 Coin 类。

        耦合中,一个类依赖于另一个类,因为它使用类的对象。

        高耦合的问题在于它会产生副作用。一个类中的一次更改可能会导致另一个类中出现意外错误,并可能破坏整个代码。

        通常,高内聚和低耦合被认为是高质量的 OOP。

        【讨论】:

          【解决方案7】:

          Cohesion(凝聚力):Co表示在一起hesion表示to坚持。将不同物质的颗粒粘合在一起的系统。

          现实生活中的例子:

          img Courtesy

          整体大于部分之和-亚里士多德。

          • 内聚度是一种序数类型的度量,通常被描述为“高内聚力”或“低内聚力”。具有高内聚性的模块往往更受欢迎,因为高内聚性与软件的几个理想特征相关联,包括健壮性、可靠性、可重用性和可理解性。相比之下,低内聚与不良特征相关,例如难以维护、测试、重用甚至理解。 wiki

          • 耦合通常与内聚形成对比。低耦合通常与高内聚相关,反之亦然。低耦合通常是结构良好的计算机系统和良好设计的标志,当与高内聚相结合时,支持高可读性和可维护性的总体目标。 wiki

          【讨论】:

            【解决方案8】:

            内聚是指类(或模块)可以做什么。低凝聚力意味着班级会采取各种各样的行动——它是广泛的,不专注于它应该做什么。高内聚意味着班级专注于它应该做的事情,即只有与班级意图相关的方法。

            低内聚示例:

            -------------------
            | Staff           |
            -------------------
            | checkEmail()    |
            | sendEmail()     |
            | emailValidate() |
            | PrintLetter()   |
            -------------------
            

            高内聚示例:

            ----------------------------
            | Staff                   |
            ----------------------------
            | -salary                 |
            | -emailAddr              |
            ----------------------------
            | setSalary(newSalary)    |
            | getSalary()             |
            | setEmailAddr(newEmail)  |
            | getEmailAddr()          |
            ----------------------------
            

            对于耦合,它是指两个类/模块之间的相关或依赖程度。对于低耦合类,改变一个类的主要内容不应该影响另一个类。高耦合会使您的代码难以更改和维护;由于类紧密结合在一起,因此进行更改可能需要对整个系统进行改造。

            好的软件设计具有高内聚低耦合

            【讨论】:

            • 我不明白删除一些方法并添加一些其他方法如何增加凝聚力。有人可以帮忙吗?
            • @SaketJain 这不仅仅是删除一些方法并添加一些其他方法。这就是方法与类目的的关系(如果解释更清楚的话)。
            • 顶部低内聚的例子看起来不错,我想你不小心说“高内聚”
            • @SaketJain Staff 类不是我们检查、发送或验证电子邮件的地方。这些函数应该放在一个假设的 Email 类中,这就是它的低内聚性的原因。在第二个示例中,Staff 类仅包含用于设置和获取 Staff 相关数据的适当信息。他们不执行应该由另一个类管理的操作。
            • @MehrajMalik,他们是相关的。但我认为Single Responsibility PrincipleHigh Cohesion & Low Coupling(后果)的原因之一。深入思考每项职责,您可以在系统中获得独立的组件,并在它们之间进行更好的沟通。
            【解决方案9】:

            耦合 = 两个模块之间的交互/关系... 内聚 = 模块内两个元素之间的交互。

            一个软件由许多模块组成。模块由元素组成。考虑一个模块是一个程序。程序中的函数是一个元素。

            在运行时,一个程序的输出被用作另一个程序的输入。这称为模块到模块的交互或进程到进程的通信。这也称为耦合。

            在单个程序中,一个函数的输出被传递给另一个函数。这称为模块内元素的交互。这也称为凝聚力。

            例子:

            耦合 = 两个不同家庭之间的交流... 凝聚力 = 家庭中父子之间的沟通。

            【讨论】:

            • 那么您如何根据它们对软件的影响来解释它们?
            • 一个软件由许多模块组成。模块由元素组成。考虑一个模块是一个程序。程序中的函数就是一个元素。
            【解决方案10】:

            内聚度是模块相对功能强度的指标。

            • 内聚模块执行单个任务,需要很少 与程序其他部分中的其他组件的交互。陈述 简单地说,一个有凝聚力的模块应该(理想情况下)只做一件事。
            • 常规观点:

              模块的“一心一意”

            • OO视图:

              内聚意味着一个组件或类只封装彼此密切相关以及与类或组件本身密切相关的属性和操作

            • 内聚程度

              功能性

              图层

              通讯

              顺序

              程序

              时间

              实用程序

            耦合表示模块之间的相对相互依赖关系。

            • 耦合取决于模块之间的接口复杂性, 对模块进行条目或引用的点,以及什么数据 穿过界面。

            • 常规视图: 一个组件与其他组件和外部世界的连接程度

            • OO 视图:对类相互连接程度的定性衡量

            • 耦合程度

              内容

              常见

              控制

              印章

              数据

              常规调用

              类型使用

              包含或导入

              外部#

            【讨论】:

              【解决方案11】:

              内聚度表示软件元素的职责之间的相关性和集中度。

              耦合是指软件元素与其他元素的连接强度。

              软件元素可以是类、包、组件、子系统或系统。在设计系统时,建议使用具有高内聚并支持低耦合的软件元素。

              低内聚度导致难以维护、理解和降低可重用性的单一类。类似地,高耦合会导致类紧密耦合,并且更改往往不是非本地的,难以更改并减少重用。

              我们可以假设我们正在设计具有以下要求的典型可监控ConnectionPool。请注意,对于像 ConnectionPool 这样的简单类来说,它可能看起来太多了,但基本意图只是通过一些简单的示例来演示 低耦合高内聚,我认为应该有帮助。

              1. 支持获取连接
              2. 释放连接
              3. 获取有关连接与使用次数的统计信息
              4. 获取有关连接与时间的统计信息
              5. 将连接检索和释放信息存储到数据库以供以后报告。

              通过低内聚,我们可以通过将所有这些功能/职责强制填充到单个类中来设计ConnectionPool 类,如下所示。我们可以看到这个类负责连接管理、与数据库交互以及维护连接状态。

              通过高度内聚,我们可以将这些职责分配给各个类,并使其更易于维护和重用。

              为了演示低耦合,我们将继续使用上面的高内聚ConnectionPool 图。如果我们看上图,尽管它支持高内聚,ConnectionPoolConnectionStatistics 类紧密耦合,PersistentStore 直接与它们交互。为了减少耦合,我们可以引入ConnectionListener 接口并让这两个类实现该接口并让它们注册到ConnectionPool 类。而ConnectionPool 将遍历这些侦听器并通知它们连接获取和释放事件,并减少耦合。

              注意/单词或警告:对于这个简单的场景,它可能看起来有点矫枉过正,但如果我们想象一个实时场景,我们的应用程序需要与多个第三方服务交互以完成交易:直接将我们的代码与第三方服务耦合意味着第三方服务中的任何更改都可能导致我们的代码在多个地方发生更改,相反我们可以让Facade在内部与这些多个服务交互以及对服务成为Facade 的本地服务,并强制与第三方服务低耦合。

              【讨论】:

              • 优秀的答案!如果可能的话,你能用其他的例子吗?连接池可能不是每个人都清楚。无论如何,它真的帮助了我。所以谢谢!
              • 使用 ConnectionListener 接口如何帮助减少耦合?你能提供一个更容易理解的例子吗?
              • @abhishekgupta 在这个例子中你可能已经注意到我们使用了观察者模式来实现低/松耦合。经历这将有助于How does Observer create loosely-coupled design?
              【解决方案12】:

              我认为差异可以如下:

              • 内聚性表示代码库的一部分形成逻辑上单一的原子单元的程度。
              • 耦合表示单个单元独立于其他单元的程度。
              • 不可能在不破坏内聚的情况下归档完全解耦,反之亦然。

              In this blog post我写的更详细。

              【讨论】:

                【解决方案13】:

                凝聚力的最佳解释来自鲍勃叔叔的清洁代码:

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

                一般来说,创建这种最大内聚性的类既不可取,也不可能;另一方面,我们希望凝聚力高。内聚度高的时候,说明类的方法和变量是相互依赖的,挂在一起成为一个逻辑整体。

                保持函数小和参数列表短的策略有时会导致实例变量被方法子集使用。发生这种情况时,几乎总是意味着至少有一个其他班级试图退出更大的班级。您应该尝试将变量和方法分成两个或多个类,以使新类更具凝聚力。

                【讨论】:

                • 我同意这可能是最好的解释,这就是我喜欢鲍勃叔叔的意思,他可以用几句话解释真正的意思。了解这个定义后,您可以立即了解应该对给定类做些什么来增加其凝聚力。
                • 这在面向对象设计的特定上下文中是一个很好的解释,但这些概念更普遍适用。此处的其他回复和(博客参考)在更一般的背景下提供了很好的解释。
                【解决方案14】:
                软件工程中的

                内聚力是某个模块的元素归属在一起的程度。因此,它是衡量一个软件模块的源代码所表达的每个功能的关联程度。

                耦合 简单来说,就是一个组件(再一次,想象一个类,虽然不一定)知道另一个组件的内部运作或内部元素,即它有多少知识另一个组件。

                I wrote a blog post about this,如果您想通过示例和图纸阅读更多详细信息。我认为它回答了你的大部分问题。

                【讨论】:

                  【解决方案15】:

                  高内聚模块内和低耦合模块之间通常被认为与OO编程语言的高质量有关。

                  例如,每个 Java 类中的代码必须具有较高的内部凝聚力,但要尽可能松散地与其他 Java 类中的代码耦合。

                  Meyer's Object-Oriented Software Construction (2nd edition) 的第 3 章很好地描述了这些问题。

                  【讨论】:

                  • 这些概念并不仅限于面向对象编程。如果有的话,我建议 OO 语言的目标是引导程序员实现高内聚/低耦合的目标。
                  【解决方案16】:

                  增加内聚和减少耦合确实会带来好的软件设计。

                  内聚对您的功能进行分区,使其简洁且最接近与其相关的数据,而解耦确保功能实现与系统的其余部分隔离。

                  解耦允许您在不影响软件其他部分的情况下更改实现。

                  内聚确保实现更具体的功能,同时更易于维护。

                  减少耦合和增加内聚的最有效方法是按界面设计

                  也就是说,主要的功能对象只能通过它们实现的接口相互“了解”。接口的实现会自然而然地引入内聚。

                  虽然在某些场景中不现实,但它应该是一个设计目标。

                  示例(非常粗略):

                  public interface IStackoverFlowQuestion
                        void SetAnswered(IUserProfile user);
                        void VoteUp(IUserProfile user);
                        void VoteDown(IUserProfile user);
                  }
                  
                  public class NormalQuestion implements IStackoverflowQuestion {
                        protected Integer vote_ = new Integer(0);
                        protected IUserProfile user_ = null;
                        protected IUserProfile answered_ = null;
                  
                        public void VoteUp(IUserProfile user) {
                             vote_++;
                             // code to ... add to user profile
                        }
                  
                        public void VoteDown(IUserProfile user) {
                            decrement and update profile
                        }
                  
                        public SetAnswered(IUserProfile answer) {
                             answered_ = answer
                             // update u
                        }
                  }
                  
                  public class CommunityWikiQuestion implements IStackoverflowQuestion {
                       public void VoteUp(IUserProfile user) { // do not update profile }
                       public void VoteDown(IUserProfile user) { // do not update profile }
                       public void SetAnswered(IUserProfile user) { // do not update profile }
                  }
                  

                  在您的代码库的其他地方,您可以有一个模块来处理问题,无论它们是什么:

                  public class OtherModuleProcessor {
                      public void Process(List<IStackoverflowQuestion> questions) {
                         ... process each question.
                      }
                  }
                  

                  【讨论】:

                    猜你喜欢
                    • 2013-01-16
                    • 1970-01-01
                    • 1970-01-01
                    • 2010-09-07
                    • 2021-06-14
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多