第3讲:高内聚低耦合到底是什么?

第3讲:高内聚低耦合到底是什么?

高内聚、低耦合。

几乎所有软件架构书都会提到这句话。

面试官喜欢问。

架构师喜欢讲。

工程师喜欢写进简历。

但现实中很多项目却是:

天天喊着高内聚低耦合,代码却越来越乱。

为什么会这样?

因为大部分人只记住了这六个字,却没有真正理解它背后的含义。

今天我们结合真实项目案例,彻底讲清楚:

  • 什么是高内聚?
  • 什么是低耦合?
  • 为什么项目会越拆越乱?
  • 如何设计真正高内聚低耦合的模块?

一、先看一个真实场景

假设现在要开发一个智能门锁。

功能很简单:

  • 按键输入密码
  • 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讲:模块化设计到底怎么做?为什么你的模块总是越拆越乱?

我们将从实际项目出发,讲清楚模块划分原则,以及很多工程师最容易踩的“伪模块化”陷阱。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

欢乐熊嵌入式编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值