软件工程中的耦合性和解耦合性是什么意思?

看《thinking in java》和《head first 设计模式》中,两本书好多次提到了“耦合性”和“解耦合性”这两个词。一直以来都是模棱两可的,搜了一下,维基百科中的解释太专业化了,我没有看懂,通俗的讲这两个词是什么意思?


谢谢大家的回答. 通过大家的回答, 我对于这两个概念有了初步的了解, 但是估计是我的层次不够吧, 看这两本书还是有难度,而且在实际自己编写代码的时候还是不能很好的运用设计模式的精髓, 不过我会努力的,谢谢大家

阅读 38.7k
9 个回答

不要一棒子把“耦合”削死。耦合是一个宽泛的概念。两个程序模块有关联就叫做耦合

某些模块必然要关联起来才能工作,这是由业务逻辑决定的,不能否认。所以解耦并不是字面意义上的把关联拆掉,而是把模块之间的关联放松到必要的程度。一些建议:

  • 模块只对外暴露最小限度的接口,形成最低的依赖关系。
  • 只要对外接口不变,模块内部的修改,就不得影响其他模块;
  • 删除一个模块,应当只影响有依赖关系的其他模块,而不应该影响其他无关部分;

软件工程有一条铁律“高内聚、低耦合”就是这个道理:必要的耦合不可否认,没有耦合程序就做不成事;但是不必要的紧耦合,就会让程序“牵一发而动全身”,最终让程序员的编写和维护都无从下手。

人类同一时间只能专注于一小部分的内容。“高内聚、低耦合”就是为了满足人类的这个特点——小尺度上只专注一个模块,局部的编码工作才能够进行。大尺度上把具体代码转化为一些抽象的“模块”和“依赖关系”,才能够抓大放小,把握住程序的脉络,拼合出一个完整的产品。

想象一下“社会大分工”这个模型——每一个小单位只专注自己的业务部分,与其他单位只存在业务外包的关联,以及物质、信息的交互。事实已经证明:这样的模型比以前大国企“大包大揽”自办各种职能部门的效率,有量级程度的提高。这就是“高内聚、低耦合”在现实世界中的体现。

程序就是人类创造的第二世界,程序的逻辑无非是世界运行规律的抽象。跳出程序看程序,就会发现不一样的观点和角度。

我也来通俗地讲一讲,说的不好请勿喷。

简单概括:耦合就是程序中的一部分跟其他部分之间的关系。解耦合就是把必要的耦合理顺,同时尽量减少不必要的耦合(这一句其实就是高内聚低耦合的通俗解释)。

如何看待耦合取决于你是从什么视角和层次看待一个程序。如果你正在关注的是一个函数或一个类的内部实现,这个粒度就很细。这个时候你关注的可能是如何把函数/类写的漂亮,让它在功能正确的同时又容易理解。你可以通过改善代码的排版、优化算法、将重复的操作封装为一个新的函数、给变量或函数取更有意义的名字等方法来改善它。

这些方法可以说都跟“高内聚低耦合”有关系。改善代码排版:让代码更有代码的样子,让人更容易看清每句代码的作用和意图。合适的名字:如果你给变量取个名字叫variable,从语意上讲它可以存储任意的值,它的模棱两可的名字让人难以分清它和其他代码的关系(与其他部分有了不必要的耦合),这就会让阅读代码的人茫然不知所措。让变量名字更有意义实际上是高内聚的一种体现,即这个变量你是想让它干嘛的,别人一眼就能看出来。提取函数就更不用说了。

如果你正在设计一个模块,你关注的可能是模块中的类和对象,也就是如何让模块中的类和对象之间的关系更加简单和清楚。四人帮的《设计模式》这本书主要就是讲这个的,其中的每一个模式都是想办法降低类和对象之间的耦合性。

那如果你正在设计一个系统呢,你可能就不关心其中的类了(至少一开始不应该关心),你应该将精力集中到这个系统可以分成几个模块、各个模块怎样组合到一起(术语称为“协作”)构成整个系统等问题上。如果你设计的模块还要去关注其他模块的实现细节,那你的系统就太失败了。

所以,不管你的角度如何,关注的层次高低,降低复杂度都是必要的。这就是耦合和解耦合的核心理念。

据说任何比喻都是蹩脚的,但我还是想举一个现实中极其高效与低耦合的例子,那就是军队。一个国家的军队少则数万人,多则数百万人,这么庞大的一个系统,它的效率却是惊人的高。为什么?简单来说这要归功于它的几个行事原则:

纪律严明,令行禁止
不会出现上级发出命令后找不到人执行,或找到人却不愿意做的尴尬情况(很内聚有木有)

命令不跨级
除非特殊情况,否则命令都是通过直接上级传达的,司令很少会单独找小兵做事(每一级在上一级看来都高内聚的,同时连长、班长、团长这些头头充当了本级的一个接口,上一级仅通过接口来向下级发命令)
有人说,如果司令直接找小兵给他做事不是比一级一级传达效率更高吗?no!为什么?原因有几条(注意看,都能跟我们编程时的道理联系上,所以说万物都是想通的)

  1. 司令发出的命令通常都是很大的任务,往往要跨越很多部队甚至兵种,如果让司令一个一个通知不是要累死?(高层抽象不应该直接与底层实现耦合)

  2. 如果允许任意的跨级下达命令,那就很有可能导致多个上级同时给一个士兵下达不同的命令,那这个士兵不是要累死?(导致系统状态不一致、或分布式系统的忙闲不一等情况)

  3. 士兵与直接上级之间的沟通通常是无障碍的,但跨级就难说了。哪个士兵擅长做什么事只有他的直接上级最清楚,跨级下达命令时可能会导致任务完成效率下降、甚至任务无法完成的后果(高层抽象往往难以驾驭底层细节,进而导致使用不当甚至误用)

职能划分明确
部队会按地域、时域、部门、兵种等来划分整个部队的职责,这样一层一层、一块一块地把整个部队分成职责明确的一个个小部分,各个部分相对独立又是一个有机整体,因此想不高效都很难啊。

你正乘坐直升机游览科罗拉多大峡谷,驾驶员——他显然犯了一个错误,在吃鱼,他的午餐——突然呻吟起来,晕了过去。幸运的是,他把你留在了离地面100英尺的地方。你推断,升降杆控制总升力,所以轻轻将其压低可以让直升机平缓降向地面。然而,当你这样做时,却发现生活并非那么简单。直升机的鼻子向下,开始向左盘旋下降。突然间你发现,你驾驶的这个系统,所有的控制输入都有次级效应。压低左手的操作杆,你需要补偿性地向后移动右手柄,并踩右踏板。但这些改变中的每一项都会再次影响所有其他的控制。突然间,你在用一个让人难以置信的复杂系统玩杂耍,其中每一项改变都会影响所有其他的输入。你的工作负担异常巨大:你的手脚在不停地移动,试图平衡所有交互影响的力量。

——《程序员的修炼之道》 我认为所有程序员都应该看,而且每年都应该重新看一遍的书

耦合性就是不必要的依赖关系,它的存在使得代码重用变得困难。例如:

一个叫 OrderService 的类负责处理订单,调用它的 createOrder() 方法会在创建订单的同时发送短信给顾客,那么这种耦合就导致,当我想创建一个订单但不想发送短信的时候,我找不到可以重用的代码,除非将发送短信的逻辑从 createOrder() 当中去掉。

我想提问者的困惑不在于找不到答案,而是答案太专业,不够通俗易懂,以上回答其实也太“专业”了。
软件中的概念往往有其在硬件中的对应,而硬件更形象化,更通俗易懂。
如果你有做电学实验的经历,这个概念就太好解释了。两块电路板之间的接线就是耦合,接线越多,电路越复杂,以至于连电路设计者自己都被绕蒙了。一旦发现错误,头绪太多很难理清,即所谓维护性变差。想往电路里接入更多板子难度也会加大,即所谓扩展性变差。就算勉强把电路跑通了,同样的接线方法也很难在其他电路中复制,即所谓复用性差。这就是高耦合的坏处。
尽管耦合有种种坏处,但是又不可避免,因为没有耦合电路就跑不起来。怎么办呢?有个解决方法就是把耦合性尽可能藏起来。一块芯片内部电路极其复杂,但是对于外界来说它的内部电路无关紧要。这种把耦合性尽可能藏起来的做法就叫解耦,对内部而言就是高内聚,对外界而言就是低耦合。逻辑效果上就是进行了一层抽象。使得外部可以把芯片视作一个整体,只需要关心它有哪些接口,而这些接口,实际上也是耦合,只不过已经是最小化的耦合了。
有了这样一层抽象之后,工程师就可以不去关心具体的电路实现,玩纯粹的逻辑。这就催生了编程语言。所以编程语言本身已经是在底层硬件解耦的基础上所做的高度抽象了。而软件工程中提到的狭义上的解耦,是指在编程语言(确切的说是用编程语言编制的程序)基础上进行更高层次的解耦,从而获得更高层次的抽象。这样一层层解耦抽象上去,包括操作系统和应用软件在内的整个软件大厦就建造出来了。但是追根溯源,耦合与解耦概念的起源还是在硬件层次上,而软件本身其实就是经过解耦的硬件基础上的抽象。
另外解耦是一个过程,在软件工程中体现为以高内聚低耦合为原则对代码进行重构。
希望我的回答对你理解耦合与解耦的概念会有所帮助。

比如前端html页面上在某个div上写onclick=foo(),就是耦合。
写$("#id").click= xxx ,就是解耦合。

耦合嘛,又不是苟合,写那么多文字也看到不喜欢。上代码:

1. 何为耦合?

func foo(){
bar()
}

func bar(){
}

foo用到了bar函数,因此,foo依赖bar。foo耦合了bar。

2. 包依赖

如果bar是另外一个包,那么在使用前,需要import,这样我们说foo也依赖bar的包。

3. 简化: 耦合≈依赖

说”耦合性就是不必要的依赖关系”,你太紧张了。

4. 为何不直接说依赖?

因为耦合听起来口味重。比如变压器一端的电流,经过“耦合”,在另一端引发电流————无直接电路连接的情况下——多酷。当年计算机科学就是物理学的一个分支,这样的新货,得有一个旧酒瓶装着。

5. 为何耦合难以理解?

耦合好理解。扩展为 内容耦合,全局耦合,耦合,blabla 就不好了解了。说一堆不给案例,更好复杂。

试着换成依赖要好很多。其实http://zh.wikipedia.org/wiki/%E8%80%A6%E5%90%88%E6%80%A7_(%E8%A8%88%E7%AE%97%E6%A9%9F%E7%A7%91%E5%AD%B8) 提到的前三个都是全局变量(资源)依赖。

6. 高耦合问题的案例

做数据库app的。天天见。

关系数据库就是外部耦合。也是全局变量的延伸。写了很多年的cs应用,不用ORM的,你改改字段名试试就知道苟合——不,强耦合关系——的痛苦了。

这里的字段就是bar,我的app就是foo。
耦合理论,研究的就是,我对bar改!改!改,却不需要foo跟着也改!改!改。越是如此,就越是低耦合。

7.低耦合的案例

这方面做的好的是win32 api。这些年的版本改改改,你的win32 app却不需要改改改,就可以自然升级。牛。

相对独立,便于单元测试,以这个为参考

比如,你家电视机显示器坏了,你会换吗?你敢换吗?应该比较复杂吧。电视机的主机和显示器设计是紧耦合的。

如果你电脑显示器坏了,你会换吗?重买一个插线换上,ok了。电脑的设计就是很松耦合的。

电脑要比电视机复杂很多,但是拆解竟然要比电视机简单很多,网卡坏了换网卡,显卡坏了换显卡。

低耦合的好处就是,方便调试,方便拆卸,替换。

同样,什么是解耦?比如你要设计一个电脑主机,在考虑如何与显示器交互的时候,遵循接口规范就行了,比如,对hdmi,还是VGA作支持就行了,不用管具体什么显示器。这在编程中就叫 面向接口编程。java里的interface起的就是这个规范的作用。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏