目录
[3.在 Callback 类中的体现](#3.在 Callback 类中的体现)
先看这样一个头文件callback.h
cpp
#ifndef CALLBACK_H
#define CALLBACK_H
// class PalmObject;
// class FaceObject;
// class PoseObject;
class Callback
{
public:
Callback();
virtual ~Callback();
// 发送关键点检测结果(如人脸、手势的关键点坐标)
virtual void sendLandmarkToLocal(long frameID, const char* data, int len) = 0;
// 发送目标人脸检测/识别结果
virtual void sendTargetFace(const char * result, int len) = 0;
// 错误回调,当处理过程中发生错误时调用
virtual void onError(int error) = 0;
// void sendDebugHandsData(PalmObject* obj, int w, int h, bool isLeft);
// void sendDebugFaceData(FaceObject* obj, int w, int h);
// void sendDebugPoseData(PoseObject* obj, int w, int h);
// 通知处理超时
void sendMsgForHandleTooLong(uint64_t dura_frame, uint64_t dura_pose, uint64_t dura_face,
uint64_t dura_hand, uint64_t dura_eulur);
// 发送性能统计信息(帧率、处理时间等)
void sendMsgForStatics(uint64_t avm_handle_frame_time, uint64_t avm_handle_pose_time,
uint64_t avm_handle_face_time, uint64_t avm_handle_hand_time,
uint64_t avm_handle_eulur_time, int input_frame_num,
int detect_pose_fps, int detect_face_fps,
int detect_hand_fps, int detect_hand_left_fps,
int detect_hand_right_fps, int detect_hand_two_fps,
int eulur_holistic_fps);
// 发送人脸特征数据
void sendTargetFaceFeature(const char * result, int len);
// 发送完整的推理结果(关键点+特征)
void sendInferceHolisticData(long frameID, long frame_duration, long ai_duration,
const char* holistic_landmark_data, int len,
const char* featureData, int featureDataLen);
// 发送后处理数据
void sendPostProcessData(uint64_t frameID, int dura,
const char* holistic_data, int holistic_len);
private:
uint64_t m_uuid;
};
#endif
我们可以发现这个C++类是一个抽象基类,主要用于定义回调接口 ,实现异步通信和事件通知机制, 它在典型的数据处理流水线(如计算机视觉、AI推理应用)中扮演着关键角色。
这个类是一个典型的例子,主要作用是为底层算法、处理模块提供一个统一的接口,使其能够向上层应用(如UI、业务逻辑层)发送处理结果、错误信息和性能数据,而无需关心上层如何具体实现这些功能。
同时在这个类中体现了两种重要的设计模式:策略模式和观察者模式,借助这个类来理解下这两个比较常见的设计模式。
观察者模式
核心思想:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
以Callback类为例:
主题(被观察者)- 处理引擎
cpp
class ProcessingEngine {
private:
Callback* observer; // 观察者
public:
void setObserver(Callback* cb) {
this->observer = cb;
}
void processFrame(const Frame& frame) {
try {
// 处理帧数据...
auto landmarks = detectLandmarks(frame);
// 通知观察者(回调)
if (observer) {
observer->sendLandmarkToLocal(frame.id, landmarks.data(), landmarks.size());
}
} catch (const exception& e) {
// 发生错误时通知观察者
if (observer) {
observer->onError(ERROR_CODE);
}
}
}
};
观察者实现
cpp
class MyApp : public Callback {
private:
ProcessingEngine engine;
public:
MyApp() {
engine.setObserver(this); // 注册为观察者
}
// 当被观察者状态变化时被调用
void sendLandmarkToLocal(long frameID, const char* data, int len) override {
updateUIWithLandmarks(data, len);
}
void onError(int error) override {
showErrorMessage(error);
}
};
这种设计模式的好处在于,处理引擎不需要知道谁在接收结果,当状态变化立即通知所有观察者,并且可以动态添加或者移除观察者。
策略模式
核心思想 :定义一系列算法,将每个算法封装起来,并且使它们可以互相替换。策略模式让算法的变化独立于使用算法的客户端。
先举一个好理解的例子:
1.算法封装和互相替换
想象有一个导航应用,它提供不同的路线规划策略 :最快路线 算法,最短路线 算法,避开收费算法。
这些不同的算法就是不同的"策略",它们可以在运行时根据用户选择动态切换,而不需要修改导航应用的核心代码。
代码示例:
1.定义策略接口:
cpp
// 策略接口 - 定义算法的共同契约
class RouteStrategy {
public:
virtual ~RouteStrategy() {}
virtual void calculateRoute(const Point& start, const Point& end) = 0;
};
2.实现具体的策略算法
cpp
// 具体策略1: 最快路线算法
class FastestRoute : public RouteStrategy {
public:
void calculateRoute(const Point& start, const Point& end) override {
cout << "Calculating fastest route from " << start << " to " << end << endl;
// 实现最快路线的具体算法
}
};
// 具体策略2: 最短路线算法
class ShortestRoute : public RouteStrategy {
public:
void calculateRoute(const Point& start, const Point& end) override {
cout << "Calculating shortest route from " << start << " to " << end << endl;
// 实现最短距离的具体算法
}
};
// 具体策略3: 避开收费算法
class AvoidTollsRoute : public RouteStrategy {
public:
void calculateRoute(const Point& start, const Point& end) override {
cout << "Calculating toll-free route from " << start << " to " << end << endl;
// 实现避开收费站的具体算法
}
};
3.上下文类--使用策略
cpp
class NavigationApp {
private:
RouteStrategy* strategy; // 持有策略对象的指针
public:
// 设置策略 - 这就是动态切换的关键!
void setStrategy(RouteStrategy* newStrategy) {
strategy = newStrategy;
}
void navigate(const Point& start, const Point& end) {
if (strategy) {
strategy->calculateRoute(start, end); // 委托给当前策略执行
}
}
};
2.运行时动态切换
cpp
int main() {
NavigationApp app;
Point start("北京"), end("上海");
// 场景1: 用户想要最快路线
FastestRoute fastest;
app.setStrategy(&fastest); // 动态设置为最快路线策略
app.navigate(start, end); // 输出: Calculating fastest route...
// 场景2: 用户改变主意,想要避开收费
AvoidTollsRoute noTolls;
app.setStrategy(&noTolls); // 动态切换为避开收费策略
app.navigate(start, end); // 输出: Calculating toll-free route...
// 场景3: 途中用户又想走最短距离
ShortestRoute shortest;
app.setStrategy(&shortest); // 再次动态切换
app.navigate(start, end); // 输出: Calculating shortest route...
return 0;
}

3.在 Callback 类中的体现
回到最初的Callback类,策略模式的应用:
cpp
// 策略接口定义
class Callback {
public:
virtual void sendLandmarkToLocal(long frameID, const char* data, int len) = 0;
virtual void sendTargetFace(const char* result, int len) = 0;
virtual void onError(int error) = 0;
// ... 其他方法
};
// 不同的处理策略
class RealTimeDisplay : public Callback {
void sendLandmarkToLocal(...) override {
// 策略1: 实时UI显示
updateDisplay(data, len);
}
};
class DataLogger : public Callback {
void sendLandmarkToLocal(...) override {
// 策略2: 数据记录和分析
logForAnalysis(frameID, data, len);
}
};
class NetworkForwarder : public Callback {
void sendLandmarkToLocal(...) override {
// 策略3: 网络传输
sendToCloud(data, len);
}
};
// 运行时动态切换
ProcessingEngine engine;
RealTimeDisplay displayStrategy;
DataLogger loggingStrategy;
// 根据应用模式动态切换回调策略
if (isDebugMode) {
engine.setCallback(&loggingStrategy);
} else {
engine.setCallback(&displayStrategy);
}
// 甚至可以在运行时根据用户操作切换
userButton.onClick([&]() {
engine.setCallback(&networkStrategy); // 动态切换!
});
每个算法被封装在独立的类中,这些类实现相同的接口,因此可以互相替换,替换发生在运行时,也不需要重新编译代码,而且可以根据配置、用户输入、系统状态等动态选择算法,这种设计方式让系统变得极其灵活和可维护。
在这个Callback类中,两种模式完美结合:观察者模式 确保当数据处理完成时及时通知上层,策略模式让上层可以自由决定如何处理接收到的数据。
两种设计模式的区别在于策略模式核心 关注的是如何做 (算法的选择与替换),观察者模式 则关注的是何时做(状态变化的通知机制)。