第2讲:什么是优秀的软件架构?

第2讲:什么是优秀的软件架构?

很多工程师写了几年代码后都会发现一个现象:

项目越来越大,

开发效率却越来越低。

新增一个功能要改十几个文件,

修复一个Bug又引出三个Bug。

最后整个项目进入一种诡异状态:

谁都不敢改代码。

那么问题来了:

到底什么样的软件架构才算优秀?


一、先看一个失败案例

几年前我接手过一个BLE智能设备项目。

第一版功能很简单:

  • BLE通信
  • OLED显示
  • 传感器采集

开发速度非常快。

但半年后项目开始失控。

增加一个蓝牙功能。

显示模块出问题。

修改显示模块。

功耗异常。

优化功耗。

OTA升级失败。

团队所有人都在说:

我只改了两行代码。


后来排查发现。

整个项目充满了这样的代码:

c 复制代码
/* ble.c */

uint8_t g_ble_connected = 0;
c 复制代码
/* display.c */

extern uint8_t g_ble_connected;

void Display_Update(void)
{
    if(g_ble_connected)
    {
        OLED_ShowString(0,0,"BLE OK");
    }
}

看起来没什么问题。

但实际上:

text 复制代码
Display
    ↓
直接依赖
    ↓
BLE

显示模块直接依赖蓝牙内部变量。

未来只要蓝牙模块变化。

显示模块一定跟着修改。

这就是典型的高耦合设计。


二、优秀架构的本质

很多人觉得:

优秀架构就是:

  • UML图画得漂亮
  • 设计模式用得多
  • 文件夹层级复杂

实际上都不是。

经过15年的项目实践。

我对软件架构的理解只有一句话:

控制复杂度

项目小时。

谁写都能跑。

项目大了以后。

真正决定项目寿命的是:

复杂度是否可控。


三、优秀架构的五个核心特征

1. 可维护

这是最重要的指标。

如果一个项目:

新增功能不敢加。

Bug不敢改。

那一定不是好架构。


例如前面的BLE案例。

优化后改成:

c 复制代码
/* ble.h */

bool BLE_IsConnected(void);
c 复制代码
/* ble.c */

static bool ble_connected = false;

bool BLE_IsConnected(void)
{
    return ble_connected;
}

显示模块:

c 复制代码
void Display_Update(void)
{
    if(BLE_IsConnected())
    {
        OLED_ShowString(0,0,"BLE OK");
    }
}

架构关系变成:

text 复制代码
Display
     ↓

BLE Interface
     ↓

BLE Internal

这样即使未来BLE内部重构。

显示模块也无需修改。


2. 可扩展

需求变化是必然的。

坏架构最大的特点就是:

新增功能必须修改旧代码。


错误设计:

c 复制代码
void APP_Task(void)
{
    GPIO_PinOutSet(gpioPortA,0);

    I2C_Transfer(...);

    USART_Tx(...);

    Flash_Write(...);
}

业务逻辑直接操作硬件。


未来如果:

text 复制代码
EFR32
 ↓
STM32
 ↓
ESP32

更换MCU。

整个项目都要改。


优秀设计应该这样:

驱动层:

c 复制代码
void LED_On(void)
{
    GPIO_PinOutSet(gpioPortA,0);
}

应用层:

c 复制代码
void Alarm_Process(void)
{
    LED_On();
}

架构:

text 复制代码
APP
 ↓

Service
 ↓

Driver
 ↓

MCU

这样未来更换芯片。

只需要修改Driver层。


3. 可测试

很多项目开发慢。

不是因为写代码慢。

而是测试成本太高。


例如修改一个BLE功能。

结果需要回归测试:

  • BLE
  • OLED
  • Sensor
  • OTA
  • Power

全部功能。


优秀架构应该支持:

c 复制代码
BLE_Test();

Sensor_Test();

Display_Test();

模块独立测试。

降低验证成本。


4. 可复用

优秀架构最大的价值之一:

重复利用。


例如:

你开发过智能手表。

后续开发:

  • 智能门锁
  • 智能温控器
  • IoT网关

很多驱动层代码都可以直接复用:

c 复制代码
UART Driver

I2C Driver

SPI Driver

Flash Driver

甚至Service层也能复用:

c 复制代码
BLE Service

Power Service

Storage Service

这才是真正的工程效率。


5. 可移植

很多项目死在这里。

业务代码直接操作硬件。

例如:

c 复制代码
GPIO_PinOutSet(gpioPortA,0);

出现在几十个业务文件里。


未来更换芯片。

直接崩溃。


正确做法:

c 复制代码
LED_On();

LED_Off();

业务层永远不接触硬件寄存器。

这样才能实现跨平台移植。


四、一个真实项目架构示例

以智能手表为例。

我们最终采用的架构如下:

应用层调用:

c 复制代码
HealthService_GetHeartRate();

PowerService_GetBattery();

BLEService_SendData();

而不会直接操作:

c 复制代码
I2C

SPI

GPIO

五、如何判断你的架构是否优秀?

我经常问团队三个问题。


问题1

新增一个功能。

需要修改几个模块?

优秀:

text 复制代码
1~2个

糟糕:

text 复制代码
10+

问题2

更换MCU需要修改多少代码?

优秀:

text 复制代码
Driver层

糟糕:

text 复制代码
整个项目

问题3

新人多久能看懂代码?

优秀:

text 复制代码
1~3天

糟糕:

text 复制代码
两周以上

六、总结

很多人把软件架构想得很复杂。

其实优秀架构并不神秘。

它不是:

  • UML图画得漂亮
  • 设计模式用得高级
  • 文件夹层级特别多

而是:

当项目规模扩大时,系统依然保持可控

记住优秀架构的五个核心特征:

✅ 可维护

✅ 可扩展

✅ 可测试

✅ 可复用

✅ 可移植

而这一切最终都指向同一个目标:

控制复杂度。


本讲思考题

你目前维护的项目中:

  1. 最大的全局变量有多少个文件在访问?
  2. 更换MCU需要改多少代码?
  3. 新增一个功能平均需要修改几个模块?

欢迎在评论区讨论。

下一讲:

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

我们将结合真实代码案例,彻底讲明白这个被无数人挂在嘴边,却很少有人真正理解的架构原则。

相关推荐
FreakStudio11 天前
W55MH32L-EVB 上手测评:硬件 TCP/IP 加持的以太网单片机,MicroPython 零门槛开发
python·单片机·嵌入式·大学生·面向对象·并行计算·电子diy·电子计算机
✎ ﹏梦醒͜ღ҉繁华落℘16 天前
单片机基础知识---stm32单片机的优先级
stm32·单片机·mongodb
zd84510150016 天前
RS485 总线详解
单片机·嵌入式硬件
牛根生同志16 天前
SPI数据收发的时候 TXE与RXNE标志位置位的时机
stm32·spi·transfer
goldenrolan16 天前
学习型红外控制系统稳定性挂测工装专项总结
软件测试·python·stm32·嵌入式·红外
✎ ﹏梦醒͜ღ҉繁华落℘16 天前
编程基础 --高内聚,低耦合
c语言·单片机
科芯创展16 天前
1A,1MHz,30VIN,XZ4115,降压恒流LED驱动芯片
单片机·嵌入式硬件
集芯微电科技有限公司16 天前
四通道2A输出集成功率电感降压模块专为紧凑型方案设计
人工智能·单片机·嵌入式硬件·生成对抗网络·计算机外设
踏着七彩祥云的小丑16 天前
嵌入式测试学习第 37 天:异常场景测试:断电、拔插、干扰、非法指令
单片机·嵌入式硬件·学习
CC城子16 天前
STM32H7_FDCAN 驱动笔记
stm32·can·canfd