第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讲:高内聚低耦合到底是什么?

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

相关推荐
嵌入式ZYXC1 小时前
第9篇:《面试题:ADC前端为什么要加运放跟随器?什么情况下可以不加?》
stm32·单片机·嵌入式硬件·面试·职场和发展
DS小龙哥1 小时前
基于STM32设计的电动车智能充电计费系统
stm32·单片机·嵌入式硬件
普中科技2 小时前
【普中STM32F1xx开发攻略--标准库版】-- 第 49 章 FLASH 字库实验
stm32·单片机·嵌入式硬件·flash·gbk·字库·普中科技
kyle~2 小时前
机器人日志系统
c++·单片机·嵌入式硬件·机器人·ros2
踏着七彩祥云的小丑2 小时前
嵌入式测试学习第 34 天:常见bug类型:死机、重启、数据错乱、通信丢包
单片机·嵌入式硬件·学习
一个嵌入式学徒2 小时前
STM32+ESP8266 接入机智云平台完整步
stm32·单片机·嵌入式硬件
SUNNYSPY0012 小时前
AO3400-ASEMI通用MOS管AI服务器专用
单片机
Zyed2 小时前
[STM32]Day12读写备份寄存器+RTC
stm32·单片机·实时音视频
芯岭技术郦3 小时前
MS32C001‑C:极致成本 32 位 MCU
c语言·开发语言·单片机