内聚和耦合有什么区别?
耦合和内聚如何导致好的或差的软件设计?
有哪些示例概述了两者之间的差异以及它们对整体代码质量的影响?
内聚和耦合有什么区别?
耦合和内聚如何导致好的或差的软件设计?
有哪些示例概述了两者之间的差异以及它们对整体代码质量的影响?
内聚是指类(或模块)可以做什么。低凝聚力意味着班级会采取各种各样的行动——它是广泛的,不专注于它应该做什么。高凝聚力意味着班级专注于它应该做的事情,即只关注与班级意图相关的方法。
低内聚的例子:
-------------------
| Staff |
-------------------
| checkEmail() |
| sendEmail() |
| emailValidate() |
| PrintLetter() |
-------------------
高凝聚力的例子:
----------------------------
| Staff |
----------------------------
| -salary |
| -emailAddr |
----------------------------
| setSalary(newSalary) |
| getSalary() |
| setEmailAddr(newEmail) |
| getEmailAddr() |
----------------------------
至于耦合,它指的是两个类/模块之间的相关或依赖程度。对于低耦合类,改变一个类的主要内容不应该影响另一个类。高耦合会使您的代码难以更改和维护;由于课程紧密结合在一起,因此进行更改可能需要对整个系统进行改造。
好的软件设计具有高内聚和低耦合。
模块内的高内聚和模块之间的低耦合通常被认为与 OO 编程语言的高质量有关。
例如,每个 Java 类中的代码必须具有较高的内部凝聚力,但要尽可能松散地与其他 Java 类中的代码耦合。
Meyer 的面向对象软件构建(第 2 版)的第 3 章很好地描述了这些问题。
内聚度表明软件元素的职责之间的相关性和集中度。
耦合是指软件元素与其他元素的连接强度。
软件元素可以是类、包、组件、子系统或系统。在设计系统时,建议使用具有高内聚和支持低耦合的软件元素。
低内聚导致难以维护、理解和降低可重用性的单一类。同样,高耦合导致类紧密耦合,并且更改往往不是非本地的,难以更改并减少了重用。
我们可以假设一个场景,我们正在设计一个ConnectionPool
具有以下要求的典型监视器。请注意,对于像这样的简单类来说,它可能看起来太多了,ConnectionPool
但基本意图只是通过一些简单的示例来展示低耦合和高内聚,我认为应该有所帮助。
在低内聚的情况下,我们可以ConnectionPool
通过将所有这些功能/职责强制填充到单个类中来设计一个类,如下所示。我们可以看到这个类负责连接管理、与数据库交互以及维护连接统计信息。
通过高度内聚,我们可以将这些职责分配给各个类,并使其更易于维护和重用。
为了演示低耦合,我们将继续使用ConnectionPool
上面的高内聚图。如果我们看上图,虽然它支持高内聚,但它与类ConnectionPool
紧密耦合,并且直接与它们交互。为了减少耦合,我们可以引入一个接口,让这两个类实现接口并让它们注册到类中。并且将遍历这些侦听器并通知它们连接获取和释放事件并允许更少的耦合。ConnectionStatistics
PersistentStore
ConnectionListener
ConnectionPool
ConnectionPool
注意/单词或警告:对于这个简单的场景,它可能看起来有点矫枉过正,但如果我们想象一个实时场景,我们的应用程序需要与多个第三方服务交互以完成事务:直接将我们的代码与第三方服务耦合这意味着第三方服务中的任何更改都可能导致我们的代码在多个位置发生更改,相反,我们可以在Facade
内部与这些多个服务进行交互,并且对服务的任何更改都成为本地的,Facade
并与第三方强制执行低耦合服务。
简单地说,内聚表示代码库的一部分形成逻辑上单一的原子单元的程度。另一方面,耦合表示单个单元依赖于其他单元的程度。换句话说,它是两个或多个单元之间的连接数。数量越少,耦合越低。
从本质上讲,高内聚意味着将彼此相关的代码库部分保存在一个地方。同时,低耦合是为了尽可能地分离代码库中不相关的部分。
从内聚和耦合的角度来看的代码类型:
理想的是遵循指南的代码。它是松散耦合和高度内聚的。我们可以用这张图来说明这样的代码:
God Object是引入高内聚和高耦合的结果。它是一种反模式,基本上代表一次性完成所有工作的一段代码: 当不同类或模块之间的边界选择不当时,就会发生选择不当
破坏性脱钩是最有趣的一种。当程序员试图解耦代码库以至于代码完全失去焦点时,有时会发生这种情况:
在这里阅读更多
增加内聚和减少耦合确实会带来好的软件设计。
内聚对您的功能进行分区,使其简洁且最接近与其相关的数据,而解耦确保功能实现与系统的其余部分隔离。
解耦允许您更改实现而不影响软件的其他部分。
内聚确保实现更具体的功能,同时更易于维护。
减少耦合和增加内聚的最有效方法是接口设计。
那就是主要的功能对象应该只通过它们实现的接口相互“了解”。接口的实现引入了内聚作为一个自然的结果。
虽然在某些场景中不现实,但它应该是一个设计目标。
示例(非常粗略):
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.
}
}
对凝聚力的最佳解释来自鲍勃叔叔的清洁代码:
类应该有少量的实例变量。一个类的每个方法都应该操作一个或多个这些变量。一般来说,一个方法操作的变量越多,该方法对它的类就越有凝聚力。每个方法都使用每个变量的类具有最大的内聚性。
一般来说,创建这种最大凝聚力的类既不可取,也不可能;另一方面,我们希望凝聚力高。当内聚度高时,意味着类的方法和变量相互依赖,并作为一个逻辑整体挂在一起。
保持函数小和参数列表短的策略有时会导致方法子集使用的实例变量的激增。发生这种情况时,几乎总是意味着至少有一个其他班级试图退出更大的班级。您应该尝试将变量和方法分成两个或多个类,以使新类更具凝聚力。
软件工程中的凝聚力是某个模块的元素属于在一起的程度。因此,它是衡量由软件模块的源代码表达的每个功能的关联程度的度量。
简单来说,耦合是一个组件(再次,想象一个类,虽然不一定)对另一个组件的内部工作或内部元素了解多少,即它对另一个组件有多少了解。
我写了一篇关于此的博客文章,如果您想通过示例和图纸阅读更多详细信息。我认为它回答了你的大部分问题。
凝聚力是模块相对功能强度的指标。
常规观点:
模块的“一心一意”
OO视图:
∎ 内聚意味着一个组件或类只封装彼此密切相关以及与类或组件本身密切相关的属性和操作
凝聚力水平
功能性
图层
通讯
顺序
程序
时间
实用程序
耦合是模块之间相对相互依赖的指示。
耦合取决于模块之间的接口复杂性、对模块进行条目或引用的点以及通过接口传递的数据。
常规视图:组件与其他组件和外部世界的连接程度
OO 视图:对类相互连接程度的定性度量
耦合程度
内容
普通
控制
印章
数据
日常通话
类型使用
包含或导入
外部#
我认为差异可以如下:
在这篇博客文章中,我更详细地写了它。
凝聚力这个词在软件设计中的含义确实有点反直觉。
凝聚力的共同含义是,能够很好地粘合在一起的东西,是统一的,其特点是分子吸引力般的强键。但是在软件设计中,这意味着要争取一个理想情况下只做一件事的类,因此甚至不涉及多个子模块。
也许我们可以这样想。当一个部分是唯一的部分(只做一件事并且不能进一步分解)时,它具有最大的凝聚力。这就是软件设计所需要的。凝聚力只是“单一责任”或“关注点分离”的另一个名称。
手头上的耦合一词非常直观,这意味着当一个模块不依赖于太多其他模块时,它所连接的那些模块可以很容易地替换,例如遵循liskov 替换原则。
简单地说,内聚意味着一个类应该代表一个单一的概念。
如果类的所有特性都与类所代表的概念相关,那么类的公共接口就是内聚的。例如,不是拥有 CashRegister 类,而是拥有 CashRegister 和 Coin 功能凝聚力使其分为 2 个类 - CashRegister 和 Coin 类。
在耦合中,一个类依赖于另一个类,因为它使用类的对象。
高耦合的问题在于它会产生副作用。一个类中的一次更改可能会导致另一个类中的意外错误,并可能破坏整个代码。
通常,高内聚和低耦合被认为是高质量的 OOP。
耦合= 两个模块之间的交互/关系... 内聚= 模块内两个元素之间的交互。
一个软件由许多模块组成。模块由元素组成。考虑一个模块是一个程序。程序中的函数是一个元素。
在运行时,一个程序的输出被用作另一个程序的输入。这称为模块到模块的交互或进程到进程的通信。这也称为耦合。
在单个程序中,一个函数的输出被传递给另一个函数。这称为模块内元素的交互。这也称为凝聚力。
例子:
耦合= 两个不同家庭之间的沟通…… 凝聚力= 一个家庭中父亲-母亲-孩子之间的沟通。
内聚表示模块内的关系,而耦合表示模块之间的关系。增加内聚对软件有好处,但避免增加耦合对软件。内聚代表模块的功能强度。耦合表示模块之间的独立性。在凝聚力中,模块专注于单一事物。在耦合中,模块连接到其他模块。因此,在设计时,您应该争取高内聚和低耦合。
1.巧合 2.逻辑 3.时间 4.程序 5.沟通 6.顺序 7.功能