高内聚、低耦合。
几乎所有软件架构书都会提到这句话。
面试官喜欢问。
架构师喜欢讲。
工程师喜欢写进简历。
但现实中很多项目却是:
天天喊着高内聚低耦合,代码却越来越乱。
为什么会这样?
因为大部分人只记住了这六个字,却没有真正理解它背后的含义。
今天我们结合真实项目案例,彻底讲清楚:
- 什么是高内聚?
- 什么是低耦合?
- 为什么项目会越拆越乱?
- 如何设计真正高内聚低耦合的模块?
一、先看一个真实场景
假设现在要开发一个智能门锁。
功能很简单:
- 按键输入密码
- OLED显示界面
- BLE通信
- 电机开锁
很多新手会这样写:
void Key_Process(void)
{
if(Key_Read() == KEY_OK)
{
OLED_ShowString(0,0,"Unlock");
BLE_Send("Door Open");
Motor_Open();
}
}
代码运行正常。
功能也实现了。
看起来似乎没有问题。
半年后。
需求增加:
- 指纹开锁
- NFC开锁
- 手机APP开锁
这时候问题来了。
因为:
Key模块
↓
OLED模块
Key模块
↓
BLE模块
Key模块
↓
Motor模块
按键模块已经知道太多事情。
新增一种开锁方式。
就必须修改Key模块。
最终变成:
if(Fingerprint_OK())
{
OLED_ShowString(...);
BLE_Send(...);
Motor_Open();
}
if(NFC_OK())
{
OLED_ShowString(...);
BLE_Send(...);
Motor_Open();
}
if(APP_OK())
{
OLED_ShowString(...);
BLE_Send(...);
Motor_Open();
}
代码开始疯狂复制。
维护成本越来越高。
这就是典型的:
低内聚、高耦合
二、什么是内聚?
先说内聚。
很多教材的解释很复杂。
其实一句话就够了:
一个模块只做一件事
例如:
LED模块:
LED_On();
LED_Off();
LED_Toggle();
UART模块:
UART_Send();
UART_Receive();
Sensor模块:
Sensor_Read();
Sensor_Init();
这样的模块职责非常明确。
这就是高内聚。
反过来看。
如果出现:
sensor.c
里面既有:
Sensor_Read();
又有:
OLED_Show();
还有:
BLE_Send();
甚至:
Flash_Write();
那么这个模块已经变成:
瑞士军刀模块
什么都干。
什么都管。
这样的模块就是:
低内聚
三、什么是耦合?
耦合描述的是:
模块之间依赖程度
举个例子。
显示模块直接访问BLE变量:
extern uint8_t g_ble_connected;
void Display_Update(void)
{
if(g_ble_connected)
{
OLED_ShowString(0,0,"BLE OK");
}
}
看起来很方便。
实际上:
Display
↓
直接依赖
↓
BLE
未来BLE状态管理发生变化:
g_ble_connected
变成:
g_ble_state
显示模块必须修改。
这就是高耦合。
四、如何降低耦合?
正确做法:
BLE模块提供接口。
bool BLE_IsConnected(void);
实现:
static bool ble_connected;
bool BLE_IsConnected(void)
{
return ble_connected;
}
显示模块:
void Display_Update(void)
{
if(BLE_IsConnected())
{
OLED_ShowString(0,0,"BLE OK");
}
}
此时关系变成:
Display
↓
BLE Interface
↓
BLE Internal
无论BLE内部如何修改。
Display都不用改变。
这就是低耦合。
五、为什么你的模块越拆越乱?
这是很多工程师都会踩的坑。
他们觉得:
模块越多越好。
于是项目变成:
led.c
led_ctrl.c
led_manage.c
led_service.c
led_process.c
一个LED拆出五个文件。
结果:
LED_Process()
↓
LED_Service()
↓
LED_Ctrl()
↓
LED_Driver()
调用链越来越长。
复杂度越来越高。
这并不是高内聚。
而是:
过度设计
真正的高内聚应该是:
LED
├── led.c
└── led.h
所有LED相关逻辑放一起。
职责明确。
足够简单。
六、一个真实项目重构案例
曾经接手过一个BLE手表项目。
项目代码量:
8万行
问题:
显示模块直接访问:
battery_level
ble_connected
heart_rate
step_count
等十几个全局变量。
架构关系:
Display
↙ ↓ ↘
BLE Sensor Power
形成网状依赖。
结果:
修改BLE功能。
显示异常。
修改电源管理。
UI异常。
后来进行重构。
统一采用Service层。
架构变成:
Display
↓
Service Layer
↙ ↓ ↘
BLE Sensor Power
显示模块调用:
BLEService_GetStatus();
PowerService_GetBattery();
HealthService_GetHeartRate();
不再访问内部变量。
重构后:
- 新功能开发效率提升40%
- Bug数量下降明显
- 新人熟悉项目时间缩短一半
七、高内聚低耦合到底长什么样?
一个优秀模块通常具备以下特征:
高内聚
模块内部:
只负责一件事
例如:
BLE_Send();
BLE_Receive();
BLE_Disconnect();
全部围绕BLE展开。
低耦合
模块外部:
只暴露必要接口
例如:
BLE_IsConnected();
BLE_SendData();
隐藏内部实现。
八、判断自己的代码是否高内聚低耦合
分享一个简单方法。
问自己三个问题。
问题1
这个模块职责是否单一?
例如:
Sensor模块
只负责传感器
如果还负责:
显示
存储
通信
说明内聚不足。
问题2
修改模块内部实现时。
外部代码是否需要修改?
如果需要。
说明耦合过高。
问题3
新增功能时。
需要修改几个模块?
优秀设计:
1~2个模块
糟糕设计:
10多个模块
九、总结
很多人把高内聚低耦合理解成一种理论。
其实它本质上是一种工程实践原则。
记住两句话:
高内聚
模块内部:
只做一件事,并把这件事做好。
低耦合
模块之间:
少知道,少依赖,少影响。
软件架构中有一个非常重要的规律:
一个模块知道得越多,它未来需要修改的概率就越大。
所以优秀架构师总是在做一件事:
让模块只关注自己的事情
当每个模块都做到这一点时。
系统复杂度就会大幅下降。
这也是高内聚低耦合真正的价值。
本讲重点
✅ 高内聚:一个模块只做一件事
✅ 低耦合:模块之间尽量少依赖
✅ 全局变量是高耦合的常见来源
✅ 接口封装是降低耦合的重要手段
✅ 高内聚低耦合是模块化设计的基础
下一讲:
第4讲:模块化设计到底怎么做?为什么你的模块总是越拆越乱?
我们将从实际项目出发,讲清楚模块划分原则,以及很多工程师最容易踩的“伪模块化”陷阱。
5万+

被折叠的 条评论
为什么被折叠?



