简单概括:耦合就是程序中的一部分跟其他部分之间的关系。解耦合就是把必要的耦合理顺,同时尽量减少不必要的耦合(这一句其实就是高内聚低耦合的通俗解释)。
如何看待耦合取决于你是从什么视角和层次看待一个程序。如果你正在关注的是一个函数或一个类的内部实现,这个粒度就很细。这个时候你关注的可能是如何把函数/类写的漂亮,让它在功能正确的同时又容易理解。你可以通过改善代码的排版、优化算法、将重复的操作封装为一个新的函数、给变量或函数取更有意义的名字等方法来改善它。
这些方法可以说都跟“高内聚低耦合”有关系。改善代码排版:让代码更有代码的样子,让人更容易看清每句代码的作用和意图。合适的名字:如果你给变量取个名字叫variable,从语意上讲它可以存储任意的值,它的模棱两可的名字让人难以分清它和其他代码的关系(与其他部分有了不必要的耦合),这就会让阅读代码的人茫然不知所措。让变量名字更有意义实际上是高内聚的一种体现,即这个变量你是想让它干嘛的,别人一眼就能看出来。提取函数就更不用说了。
如果你正在设计一个模块,你关注的可能是模块中的类和对象,也就是如何让模块中的类和对象之间的关系更加简单和清楚。四人帮的《设计模式》这本书主要就是讲这个的,其中的每一个模式都是想办法降低类和对象之间的耦合性。
那如果你正在设计一个系统呢,你可能就不关心其中的类了(至少一开始不应该关心),你应该将精力集中到这个系统可以分成几个模块、各个模块怎样组合到一起(术语称为“协作”)构成整个系统等问题上。如果你设计的模块还要去关注其他模块的实现细节,那你的系统就太失败了。
所以,不管你的角度如何,关注的层次高低,降低复杂度都是必要的。这就是耦合和解耦合的核心理念。
据说任何比喻都是蹩脚的,但我还是想举一个现实中极其高效与低耦合的例子,那就是军队。一个国家的军队少则数万人,多则数百万人,这么庞大的一个系统,它的效率却是惊人的高。为什么?简单来说这要归功于它的几个行事原则:
纪律严明,令行禁止
不会出现上级发出命令后找不到人执行,或找到人却不愿意做的尴尬情况(很内聚有木有)
命令不跨级
除非特殊情况,否则命令都是通过直接上级传达的,司令很少会单独找小兵做事(每一级在上一级看来都高内聚的,同时连长、班长、团长这些头头充当了本级的一个接口,上一级仅通过接口来向下级发命令)
有人说,如果司令直接找小兵给他做事不是比一级一级传达效率更高吗?no!为什么?原因有几条(注意看,都能跟我们编程时的道理联系上,所以说万物都是想通的)
-
司令发出的命令通常都是很大的任务,往往要跨越很多部队甚至兵种,如果让司令一个一个通知不是要累死?(高层抽象不应该直接与底层实现耦合)
-
如果允许任意的跨级下达命令,那就很有可能导致多个上级同时给一个士兵下达不同的命令,那这个士兵不是要累死?(导致系统状态不一致、或分布式系统的忙闲不一等情况)
-
士兵与直接上级之间的沟通通常是无障碍的,但跨级就难说了。哪个士兵擅长做什么事只有他的直接上级最清楚,跨级下达命令时可能会导致任务完成效率下降、甚至任务无法完成的后果(高层抽象往往难以驾驭底层细节,进而导致使用不当甚至误用)
职能划分明确
部队会按地域、时域、部门、兵种等来划分整个部队的职责,这样一层一层、一块一块地把整个部队分成职责明确的一个个小部分,各个部分相对独立又是一个有机整体,因此想不高效都很难啊。
某些模块必然要关联起来才能工作,这是由业务逻辑决定的,不能否认。所以解耦并不是字面意义上的把关联拆掉,而是把模块之间的关联放松到必要的程度。一些建议:
- 模块只对外暴露最小限度的接口,形成最低的依赖关系。
- 只要对外接口不变,模块内部的修改,就不得影响其他模块;
- 删除一个模块,应当只影响有依赖关系的其他模块,而不应该影响其他无关部分;
软件工程有一条铁律“高内聚、低耦合”就是这个道理:必要的耦合不可否认,没有耦合程序就做不成事;但是不必要的紧耦合,就会让程序“牵一发而动全身”,最终让程序员的编写和维护都无从下手。
人类同一时间只能专注于一小部分的内容。“高内聚、低耦合”就是为了满足人类的这个特点——小尺度上只专注一个模块,局部的编码工作才能够进行。大尺度上把具体代码转化为一些抽象的“模块”和“依赖关系”,才能够抓大放小,把握住程序的脉络,拼合出一个完整的产品。
想象一下“社会大分工”这个模型——每一个小单位只专注自己的业务部分,与其他单位只存在业务外包的关联,以及物质、信息的交互。事实已经证明:这样的模型比以前大国企“大包大揽”自办各种职能部门的效率,有量级程度的提高。这就是“高内聚、低耦合”在现实世界中的体现。
程序就是人类创造的第二世界,程序的逻辑无非是世界运行规律的抽象。跳出程序看程序,就会发现不一样的观点和角度。