0 摘要
回調函數適合簡單、一對一的快速響應,比如攝像頭採集完直接觸發日誌記錄;
觀察者模式適用於本地多模塊聯動,比如激光雷達數據同時供感知、定位和日誌模塊使用,互不干擾還易擴展;
發佈/訂閲模式通過中間件解耦,實現異步、跨系統通信,是大型無人車平台,如Apollo CyberRT的核心,適合OTA升級、數據統計等大規模、多目標場景。
這些模式本質上是不同程度的解耦策略,三者各有側重,小用回調、中用觀察者、大用Pub/Sub。
0.1 演進關係
回調函數
↓ 擴展為多訂閲者
觀察者模式
↓ 引入中間件解耦
發佈/訂閲模式
0.2 選型建議
- 簡單的UI交互 → 回調函數
- 模塊間的狀態監聽 → 觀察者模式
- 系統間解耦通信 → 發佈/訂閲模式
這些模式本質上是不同程度的解耦策略,按需選擇即可。
1 概念與特點
| 特性 | 觀察者模式(Observer) | 發佈/訂閲模式(Pub/Sub) | 回調函數(Callback) |
|---|---|---|---|
| 定義 | 被觀察對象維護多個觀察者(監聽者)列表,狀態變化時主動通知所有已註冊的observer | 發佈者發送消息到消息代理/事件總線,中間件轉發給所有該主題的訂閲者,解耦了發佈者和訂閲者 | 回調是一種編程技術,將一個可執行對象(如函數指針,或lambda表達式)作為參數傳遞,在特定事件發生時被調用 |
| 耦合度 | 中(觀察者知道主題) | 低(通過中間件解耦):發佈者和訂閲者互相不知道對方存在 | 高(直接依賴) |
| 通信方式 | 同步調用:需要實現統一的update方法 | 異步:通常通過消息隊列或事件總線 | 同步或異步 |
| 關係 | 一對多 | 多對多 | 一對一(通常) |
| 複雜性 | 中等 | 高(需要消息系統) | 低 |
| 使用場景 | GUI事件、數據監聽 | 微服務、分佈式系統;可以按主題過濾消息;可以跨進程,甚至分佈式部署,是現代無人車平台基礎架構之一,如Apollo CyberRT,等 | 簡單異步通知、事件處理 |
| 伸縮性 | 有限(所有觀察者一起通知) | 好(可動態增減訂閲者) | 差(硬編碼關係) |
| 優點 | 支持多目標聯動、便於功能拓展 | 擴展性好,低耦合、支持分佈式部署 | 實現簡單、效率高 |
| 缺點 | notify過程可能阻塞主線程 | 引入中間件複雜度高、調試難度增加 | 擴展性差、耦合度高 |
2 典型場景
| 場景 | 推薦模式 | 描述 |
|---|---|---|
| 日誌記錄 | 三種均可 | 每當關鍵數據流或異常發生時,通過回調/observer/subscriber寫入日誌 |
| 異常報警 | Callback,或Pub/Sub | 硬件故障實時推送報警信息;安全策略統一響應 |
| 數據統計分析 | Pub/Sub | 後台統計模塊匯聚各路指標,為運維團隊提供健康報告 |
| OTA升級廣播 | Pub/Sub | 新固件上線後批量通知車輛終端依次下載更新 |
| UI界面刷新 | Observer | 路徑規劃結果產生,即時渲染路線信息 |
| 單設備採集處理 | Callback | 攝像頭採集完成,快照並同步輸出日誌 |
3 UML類圖
3.1 回調函數


+------------------+
| SensorDriver |
+------------------+
| + setCallback() -----> (Function Pointer / std::function)
+------------------+
3.2 觀察者模式

3.3 發佈/訂閲 Pub/Sub

EventBUS/Broker為消息總線,可以是Channel/EventBus/MQ,等。
4 C++ Demo
4.1 回調函數 Demo —— 日誌記錄&異常監控
#include <iostream>
#include <functional>
class CameraDriver {
public:
void setFrameCallback(std::function<void(int)> cb) {
callback_ = cb;
}
void captureFrame(int frameId) {
// 通知外部業務,比如寫日誌
if (callback_) {
callback_(frameId);
}
}
private:
std::function<void(int)> callback_;
};
void logFrameEvent(int id) {
std::cout << "[LOG] Received camera frame: " << id << "\n";
}
int main() {
CameraDriver driver;
driver.setFrameCallback(logFrameEvent);
driver.captureFrame(101);
}
適用場景:“單設備數據流,一對一響應”,如攝像頭採集完成直接打一次快照並同步輸出日誌。
4.2 觀察者模式 Demo —— 多功能協同+日誌
#include <iostream>
#include <vector>
#include <memory>
class PointCloudObserver {
public:
virtual void update(const std::string& cloudInfo) = 0;
};
class PointCloudManager {
public:
void attach(std::shared_ptr<PointCloudObserver> obs) {
observers_.push_back(obs);
}
// use remove
void detach(const std::shared_ptr<PointCloudObserver>& obs) {
observers_.erase(std::remove(observers_.begin(), observers_.end(), obs),
observers_.end());
}
// use remove_if
void detach(const std::shared_ptr<PointCloudObserver>& obs) {
observers_.erase(
std::remove_if(observers_.begin(), observers_.end(),
[&] (const std::shared_ptr<PointCloudObserver>& o) {
return o == obs;
}),
observers_.end()
);
}
// notify
void newPointCloudArrived(const std::string& info) {
for(auto &obs : observers_) {
obs->update(info);
}
}
private:
std::vector<std::shared_ptr<PointCloudObserver>> observers_;
};
class PerceptionModule : public PointCloudObserver {
void update(const std::string &info) override {
std::cout << "Perception got pointcloud: " << info << "\n";
}
};
class LocalizationModule : public PointCloudObserver{
void update(const std::string &info) override {
std::cout << "Localization got pointcloud: " << info << "\n";
}
};
class LogModule : public PointCloudObserver {
void update(const std::string &info) override {
// 實際項目應寫文件,這裏演示輸出即可
std::cout << "[LOG] Event received, content: " << info << "\n";
}
};
int main() {
auto manager = std::make_shared<PointCloudManager>();
manager->attach(std::make_shared<PerceptionModule>());
manager->attach(std::make_shared<LocalizationModule>());
manager->attach(std::make_shared<LogModule>());
manager ->newPointCloudArrived("Lidar Frame #42");
}
適用場景:“本地功能協同,多業務並行”,如激光雷達幀既要供感知,又要定位,還要留痕,都只需掛載對應observer而無需改主體代碼邏輯,大大提升迭代效率和穩定性。
4.3 發佈/訂閲 Demo —— 全局異步+多目標+集中Logger服務
#include <iostream>
#include <map>
#include <vector>
#include <functional>
#include <string>
#include <mutex>
#include <algorithm>
class EventBus {
public:
using Callback = std::function<void(const std::string&)>;
void subscribe(const std::string& topic, Callback cb) {
std::lock_guard<std::mutex> lock(mtx_);
subs_[topic].push_back(cb);
}
void publish(const std::string& topic, const std::string& msg) {
std::lock_guard<std::mutex> lock(mtx_);
for(auto &cb : subs_[topic]) {
cb(msg);
}
}
private:
std::map<std::string, std::vector<Callback>> subs_;
std::mutex mtx_;
};
void perceptionHandler(const std::string& msg) {
std::cout << "Perception received: " << msg << std::endl;
}
void planningHandler(const std::string& msg) {
std::cout << "Planning received: " << msg << std::endl;
}
// Logger subscriber,可以把所有event落盤或上傳遠程服務器
void loggerHandler(const std::string& msg) {
// 演示直接打印,也可以改成logToFile(msg)
std::cout << "LOGGER EVENT: " << msg << "\n";
}
int main() {
EventBus bus;
bus.subscribe("pointcloud", perceptionHandler);
bus.subscribe("route", planningHandler);
// 所有pointcloud相關也會被logger捕捉
bus.subscribe("pointcloud", loggerHandler);
bus.subscribe("route", loggerHandler);
bus.publish("pointcloud", "new lidar frame");
bus.publish("route", "updated path info");
}
適用場景:“全局異步、多目標、大規模拓撲”,比如感知節點通過“obstacle”頻道廣播檢測結果,下游任意數量子系統都能隨時動態接收,不影響主流程結構,同時方便加入各種輔助服務——包括在線診斷、安全策略、人機交互以及海量運營級別的數據統計分析。
5 小結建議
1 對於簡單任務、一對一快速反應,用回調即可;如硬件驅動層錯誤報警直連Logger接口;
2 本地多模塊聯動且需要靈活增刪監聽方,用觀察者;例如激光雷達幀同時供多個算法使用又不相互干擾,提高開發效率和可靠性;
3 大規模無人車平台務必採用標準化Event Bus,如ROS Topic/Cyber Channel,以保證橫向擴展能力和容錯性,同時方便引入各種輔助服務,包括在線診斷、安全策略、人機交互以及大數據統計分析;
4 對於邊緣計算需求,還可藉助這些機制快速開發OTA升級推送、新版本灰度測試,以及用户行為埋點採集,等。