很多大型系统(工业软件、机器人系统、自动驾驶、复杂 Qt 应用)在规模变大以后,都会逐渐引入 事件驱动架构(Event Bus / Event Driven Architecture)。
原因很简单:
当系统模块越来越多时,模块之间的调用关系会爆炸式增长,最终变得无法维护。
我用一个逐步演化的真实思维过程来解释。
一、最初阶段:直接调用(最直观)
系统刚开始只有几个模块:
- UI
- MotorController
- CameraController
调用关系很简单:
UI → MotorController
UI → CameraController
代码类似:
cpp
void MainWindow::onStartClicked()
{
motorController.start();
cameraController.capture();
}
问题不大。
二、系统变大:调用关系开始爆炸
后来系统增加很多模块:
- MotorController
- CameraController
- LightController
- NetworkController
- DataLogger
- AlarmSystem
- UI
此时如果还是直接调用,会变成:
UI → MotorController
UI → CameraController
UI → LightController
MotorController → Logger
MotorController → Alarm
CameraController → Logger
CameraController → Network
Network → Controller
模块之间的关系变成一个 蜘蛛网结构:
A → B
A → C
B → D
C → D
D → E
E → B
这叫:
强耦合系统
问题:
1️⃣ 修改一个模块可能影响很多模块
2️⃣ 新增功能需要改很多代码
3️⃣ 很难理解系统结构
三、真实案例:增加一个"报警系统"
假设系统需要增加一个新模块:
AlarmSystem
需求:
- 电机过载报警
- 温度过高报警
- 网络断开报警
如果是直接调用:
MotorController 要加:
cpp
if(overload)
alarm.raise("motor overload");
CameraController:
cpp
if(cameraError)
alarm.raise("camera error");
NetworkController:
cpp
if(disconnect)
alarm.raise("network error");
问题:
❌ 每个模块都要修改
❌ 未来新增模块还要继续改
这就是 耦合地狱。
四、工程师的思考:能不能让模块不互相认识?
工程师开始思考:
如果模块之间不直接调用,而是发布消息,会不会更好?
于是出现:
事件驱动架构
五、事件驱动架构的核心思想
模块之间不直接调用。
而是:
发布事件
和
订阅事件
通过一个 EventBus(事件总线) 连接。
结构变成:
Module A → EventBus → Module B
模块之间 互相不知道对方存在。
六、例子:电机完成运动
传统调用:
MotorController → CameraController
代码:
cpp
motorController.moveDone();
cameraController.capture();
问题:
MotorController 必须知道 CameraController。
事件驱动:
MotorController → EventBus
CameraController ← EventBus
代码:
发布事件:
cpp
eventBus.publish("MotorMoveDone");
订阅事件:
cpp
eventBus.subscribe("MotorMoveDone",
[](){
camera.capture();
});
MotorController 不知道 CameraController。
七、增加新功能时的变化
假设我们新增:
Logger
记录电机完成事件。
传统方式:
需要修改:
MotorController
CameraController
事件方式:
只需要:
Logger 订阅事件
代码:
cpp
eventBus.subscribe("MotorMoveDone",
[](){
logger.log("motor finished");
});
原代码 完全不需要改。
八、再举一个真实设备流程
自动检测设备流程:
移动平台
→ 拍照
→ 图像检测
→ 上传结果
传统调用:
MotionController
↓
CameraController
↓
InspectionController
↓
NetworkController
代码:
cpp
motion.move();
camera.capture();
auto result = inspect.detect();
network.send(result);
模块互相依赖。
事件驱动:
MotionController
↓
Event: MoveDone
CameraController
↓
Event: ImageCaptured
InspectionController
↓
Event: InspectionDone
结构:
MotionController
↓
EventBus
↓
CameraController
↓
EventBus
↓
InspectionController
九、系统结构变化
没有 EventBus:
A → B → C → D
有 EventBus:
A → EventBus
B → EventBus
C → EventBus
D → EventBus
模块只依赖:
EventBus
系统耦合大幅下降。
十、再举一个复杂例子(工业软件)
事件:
TemperatureHigh
订阅者可能有:
AlarmSystem
Logger
UI
CoolingController
发布者:
SensorController
代码:
发布:
cpp
eventBus.publish("TemperatureHigh");
订阅:
cpp
eventBus.subscribe("TemperatureHigh",
[](){ alarm.trigger(); });
eventBus.subscribe("TemperatureHigh",
[](){ logger.log(); });
eventBus.subscribe("TemperatureHigh",
[](){ ui.showWarning(); });
新增功能只需要:
新增订阅者
十一、事件驱动带来的巨大好处
1 模块解耦
模块互相不认识。
2 可扩展
新增功能:
新增订阅者
不用修改旧代码。
3 更符合现实世界
现实世界就是事件驱动:
门打开 → 灯亮
门打开 → 摄像头启动
门打开 → 报警系统记录
4 易于并发
事件可以:
异步处理
多线程处理
十二、Qt 为什么非常适合事件架构
Qt 本身就是 事件驱动框架。
例如:
Signal → Slot
本质就是:
Event → Listener
例子:
cpp
connect(button,
&QPushButton::clicked,
this,
&MainWindow::onClicked);
这其实就是一个小型 EventBus。
十三、大型系统为什么几乎都用事件架构
因为系统规模一大,就会出现:
模块数量 ↑
调用关系 ↑↑
复杂度 ↑↑↑
EventBus可以把:
N² 的调用关系
变成:
N 的关系
这是复杂度降低的关键。
十四、一句话理解 EventBus
普通架构:
模块直接打电话
事件架构:
模块在广播电台发布消息
需要的人自己收听