代码腐烂的元凶:不是技术债务,是耦合
Site Owner
发布于 2026-05-27
为什么你的系统改完就崩?不是代码烂,是模块之间的关系乱了。七个耦合类型,从非直接耦合到内容耦合,看看你的代码在哪个级别。

代码腐烂的元凶:不是技术债务,是耦合
你以为系统崩是因为代码烂?错了。问题在模块之间的关系——而这个关系,有个听起来很无害的名字:耦合。
很多程序员以为代码质量差是"写的问题"。其实大部分系统死掉,不是因为某个人写了烂代码,而是因为模块之间的关系乱成了一团毛线。你改一个模块,另一模块莫名其妙挂了。再改,再崩。最后没人敢动。
这就是耦合在作祟。
七种耦合,从"完全不熟"到"直接私奔"
耦合听起来抽象,其实就是问:两个模块之间,谁知道谁多少?
按严重程度,从低到高排是这样的:
1. 非直接耦合:最理想,但基本做不到
两个模块完全不直接通信,必须通过主模块转发。比如A模块和B模块完全不直接说话,都找C协调。
这种情况独立性强,但实际系统里几乎不存在。因为完全解耦意味着没有直接调用,这在真实业务里是不可能的。
2. 数据耦合:正常、健康、可接受
A调用B,只传参数。比如getUserName(userId),只传一个ID,返回名字。
简单数据往来,不牵扯复杂结构。这种耦合是工程实践中应该追求的日常状态。
3. 标记耦合:危险边缘,开始埋雷
A调用B,传递的不是简单参数,而是一个数据结构或对象本身。
比如传一个User对象,而不是userId。表面看是"一次传完省事",但这意味着A需要知道B期待什么格式。一旦B改了User结构,A也得改。
你以为自己是在传数据,其实你是在传一个定时炸弹。
4. 控制耦合:已经开始互相影响了
一个模块直接告诉另一个模块"你应该怎么做"。比如传一个控制标志isAdmin=true,B看到这标志就知道该做什么逻辑。
这时候两个模块不是"我给你数据、你处理",而是"我在指挥你"。
5. 外部耦合:全局变量的诱惑
所有模块都访问同一个全局变量。比如大家都在读CONFIG.settings。
这事儿太常见了——CONFIG对象到处扔,到处改,到最后没人知道谁改的、为什么改。
6. 公共耦合:共享一个大澡堂
几个模块共享一个全局数据结构,比如同一个共享内存区域或全局数据库表。
一个模块写了,另一模块立刻能读到。听起来爽,但只要有一个模块修改了数据格式,其他全部中枪。
7. 内容耦合:最严重的背叛,直接偷看内衣
一个模块直接读另一个模块的内部数据,或者直接跳到另一个模块内部执行。
比如直接修改另一个函数的局部变量,或者直接goto到另一个函数的代码段。
这种耦合在现代编程里几乎等同于犯罪。
为什么你的系统改完就崩?耦合是元凶
举一个真实场景。
产品说:用户模块需要加一个"VIP等级"字段。你找到User结构体,加了一行。编译通过了。
然后你发现:订单模块崩了,因为它在某个地方直接解析了User对象的字节偏移,把等级字段当成了价格。缓存模块也崩了,因为它存的是User的二进制序列化。日志模块也出问题,因为日志格式依赖User的大小。
这个例子是我编的,但性质一模一样。现实里这种"改一处、爆十处"的故事每天都在上演。
原因很简单:你以为你在改一个模块,其实你在改一个耦合网络。标记耦合、数据结构耦合,在系统里扩散的范围远超你的想象。
一个健康的系统,每个模块应该像一个独立的公司——我只跟你签合同,你给我输入、输出,不多不少。我不需要知道你内部怎么运作。
架构师每天都在问的问题:怎么降耦?
知道了七种耦合,问题是怎么在实际操作中保持低耦合?
第一招:信息隐藏。
每个模块只暴露必要的接口,隐藏内部实现。传参数不要传对象,要传基础类型。实在需要传结构,只传必要的部分,不要传整个对象。
第二招:依赖注入。
A依赖B,不要让A直接new B,而是通过构造函数或参数把B传进来。这样测试的时候可以mock,部署的时候可以换实现。
第三招:接口抽象。
模块之间通过接口通信,而不是具体实现。Java的interface、C++的抽象类,都是这个思路。接口变了,实现可以随时换。
第四招:事件驱动。
不用直接调用,而是通过发布-订阅机制。模块A发布一个事件,模块B订阅这个事件。AB之间不需要知道彼此存在。
为什么微服务火?因为它是物理降耦
很多人以为微服务是因为"快"。其实微服务最大的价值是物理隔离耦合。
单体架构里,所有模块在一个进程内。传对象、共享内存、太容易。标记耦合、公共耦合到处都是,因为成本太低了。
微服务强制你通过HTTP/API通信。传结构?序列化/反序列化。传控制信息?得写进协议里。共享全局变量?跨进程根本做不到。
这些约束逼着团队把耦合降下来。不是微服务技术厉害,是它的约束让坏设计活不下去。
所以呢?
耦合不是"知道一下就行"的概念。它是软件系统腐烂的根本原因。
你的系统能不能改、敢不敢改、会不会改完就爆——取决于模块之间的关系有多乱。
下次你看到代码改一个地方爆十个地方,先别急着骂写代码的人。先看看模块之间是不是耦合太重了。
重构的第一步不是重写代码,是重新梳理模块之间的依赖关系。
考点来源:系统架构设计师 43 — 模块独立性的度量(耦合)