我最近注意到去耦是问题中的一个话题,并且想知道它是什么以及它可以在哪里应用。
“在哪里可以适用”,我的意思是:
它仅与涉及C和Java之类的编译语言有关吗?
我应该作为Web开发人员了解/研究它吗?
我最近注意到去耦是问题中的一个话题,并且想知道它是什么以及它可以在哪里应用。
“在哪里可以适用”,我的意思是:
它仅与涉及C和Java之类的编译语言有关吗?
我应该作为Web开发人员了解/研究它吗?
Answers:
“耦合”是一个术语,描述了软件系统中两个实体(通常是类)之间的关系。
当一个类使用另一个类或与其通信时,据说它“依赖”该另一个类,因此这些类是“耦合的”。他们中至少有一个“知道”另一个。
我们的想法是,我们应尽量使系统中各类之间的耦合尽可能“松散”:因此,“松散耦合”或有时是“解耦”(尽管英语中的“解耦”意味着“根本没有耦合”,人们通常用它来暗示实体之间的“松散耦合”)。
那么:在实践中什么是松散耦合与强耦合?为什么要使实体松散耦合?
耦合描述了一个实体与另一实体之间的依赖程度。通常是类或对象。
当ClassA严重依赖ClassB时,更改ClassB时ClassA受到影响的机会很高。这是强耦合。
但是,如果ClassA轻微依赖ClassB,则ClassA代码更改以任何方式影响ClassA的机会就很小。这是松散耦合,或“解耦”关系。
松散耦合是好的,因为我们不希望我们系统的组件彼此严重依赖。我们希望保持系统模块化,在这里我们可以安全地更改一个零件而又不影响另一个零件。
当两个部分松散耦合时,它们彼此之间更独立,并且在其他部分更改时不太可能破裂。
例如,建造汽车时,您不希望引擎的内部变化会破坏方向盘上的东西。
尽管这在制造汽车时绝不会偶然发生,但程序员始终会遇到类似的情况。松散耦合的目的是减少此类情况发生的风险。
强耦合通常在实体A知道太多关于实体B发生如果实体A做出有关如何实体B操作或者它是如何建成,比有一个高风险,在实体B的变化将影响到实体A.太多的假设这这是因为关于实体B的假设之一现在不正确。
例如,想象一下,作为一名驾驶员,您将对汽车发动机的工作原理做出某些假设。
当您购买的新汽车的发动机工作原理不同(或由于某种原因而更换了发动机)时,您以前的假设是错误的。如果您使用的是计算机中的代码,那么您现在将是无法正常工作的不正确代码。
但是,如果您作为驾驶员对汽车所做的所有假设都是:A-他们有方向盘,B-他们有刹车和油门踏板,那么只要您的一些假设,汽车的变化不会影响您保持正确。这是松耦合。
实现松散耦合的一项重要技术是封装。这个想法是,一个类对其他类隐藏其内部细节,并提供一个严格定义的接口供其他类与其通信。
因此,举例来说,如果你定义一个类车,它的接口(公共方法)可能会是drive()
,stop()
,steerLeft()
,steerRight()
,getSpeed()
。这些是其他对象可以在Car对象上调用的方法。
Car类的所有其他详细信息:发动机的工作方式,其使用的燃料种类等对其他类都是隐藏的,以防止他们对Car过多了解。
当A类对B类了解太多时:我们之间存在紧密耦合的关系,其中A类过于依赖B类,而B类的更改很可能会影响A类。这使得系统难以扩展和维护。
两个实体之间的关系(它们彼此之间很少了解(仅需要知道))是松散耦合或分离的关系。
去耦通常是查看两个事物是否需要紧密协作或可以进一步独立。独立之所以伟大,是因为它使这些事情易于更改或在其他地方使用。去耦不仅可以应用于发展,还可以应用于很多领域,对于时间上的去耦尤其如此,它甚至可以应用到您的日常生活中(一定程度上)。还应注意,在软件开发中要考虑多种类型的耦合,并且答案中不会涵盖所有耦合。您将必须进行研究。
如果两者之间存在依赖关系,则将A和B两件事结合在一起。如果A依赖于B,则可以在不考虑A的情况下使用B。如果要使用A,则还必须随身携带B,因为A取决于它。
通常,在软件开发中,您无法完全消除组件之间的耦合。在这种情况下,去耦通常意味着放松现有的耦合。也就是说,请确保每个组件对周围的其他组件了解得尽可能少。
这可能是在野外发现的最常见的耦合类型:
//Inside a class meant to display info for a particular Facebook user.
void DisplayFriends(FacebookUser user, FacebookClient client)
{
Friend[] = (Friend[])client.GetConnection().ExecuteCommandForResult("getFriends:" + user.Name);
...
}
在这里,DisplayFriends无需手动操作Facebook客户端背后的连接即可获得所需的内容。DisplayFriends
的类被耦合至两个FacebookClient
和Connection
。如果FacebookClient突然更改其使用的连接类型,则该应用将无法再与Facebook好友取得联系。通过要求FacebookClient提供我们所需的内容,我们可以消除DisplayFriends类和Connection之间的耦合。
//Inside a class meant to display info for a particular Facebook user.
void DisplayFriends(FacebookUser user, FacebookClient client)
{
Friend[] = client.GetFriends(user);
...
}
现在,我们实际上并不在乎FacebookClient如何获得我们的朋友。我们真正关心的只是事实,它得到了他们。内部行为的任何改变都不会损害我们自己的班级。
通过遵循功能的德米特定律,可以很容易地实现这种解耦(引用维基百科):
函数的Demeter定律要求对象O的方法m只能调用以下类型的对象的方法:
- O本身
- m的参数
- 在m内创建/实例化的任何对象
- O的直接组成对象
- 全局变量,在m范围内可由O访问
由于我在答案的开头提到了时间耦合,因此我将简要介绍一下。
在设计并行执行算法的应用程序时,通常考虑时间耦合。考虑两个可执行单元(它们是实际的指令,方法,或您希望将其视为单元的任何内容),A和B。如果B必须在执行A之前执行,则A暂时耦合到B。如果A在时间上没有与B耦合,则A和B可以同时执行。为此建立一个自己的例子:考虑一下您日常生活中的平常工作。您有一件一件一件一件接一件的事情,但是您可以同时做吗?
最后,回答您的最后一点:
我应该作为Web开发人员了解/研究它吗?
是。例如,最流行的Web开发设计模式(MVC)之一就是解耦。
其他答案很好地描述了去耦。我想讨论你的最后一个问题。
我应该作为Web开发人员了解/研究它吗?
答案是肯定的。您是哪种类型的开发人员都没有关系。您可能是Web开发人员或嵌入式系统开发人员。
假设您有一个类,该类生成放置在您的网页上的自定义URL。您可能很想让该类将URL直接写到页面上,然后将它们放在标记的href中。起初,这似乎更简单。但是,当您需要进行更改以将链接放入电子邮件并将其发送给用户时,会发生什么情况呢?您当前的代码无法使用,因为您的URL生成器与您使用的Web框架紧密相关。
更好的选择是拥有一个URL生成器类,该类将URL作为字符串返回。您的网络代码可以使用它,电子邮件代码也可以使用它。解耦代码似乎需要更多的前期工作,但是随着时间的推移,付出的努力是值得的。
不管您在哪个编程领域中工作,模块化和分离的代码都将使您的代码更易于理解,可测试和可维护性更高。