当前位置: 首页 > news >正文

C++设计模式--策略模式与观察者模式

       

目录

观察者模式

策略模式

1.算法封装和互相替换

2.运行时动态切换

3.在 Callback 类中的体现

 


 

 

先看这样一个头文件callback.h

#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类为例:

主题(被观察者)- 处理引擎


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);}}}
};

观察者实现


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.定义策略接口:

// 策略接口 - 定义算法的共同契约
class RouteStrategy {
public:virtual ~RouteStrategy() {}virtual void calculateRoute(const Point& start, const Point& end) = 0;
};

2.实现具体的策略算法

// 具体策略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.上下文类--使用策略

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.运行时动态切换

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类,策略模式的应用:

// 策略接口定义
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类中,两种模式完美结合:观察者模式确保当数据处理完成时及时通知上层,策略模式让上层可以自由决定如何处理接收到的数据。

        两种设计模式的区别在于策略模式核心关注的是如何做(算法的选择与替换),观察者模式则关注的是何时做(状态变化的通知机制)。

 

 

http://www.xdnf.cn/news/1339345.html

相关文章:

  • 安卓app、微信小程序等访问多个api时等待提示调用与关闭问题
  • QT QProcess, WinExec, ShellExecute中文路径带空格程序或者脚本执行并带参数
  • 灵活使用UE5 Modeling中的UV编辑功能
  • QT-初识
  • 日志收集(ELK)
  • javaweb开发笔记——微头条项目开发
  • 【笔记】Facefusion3.3.2 之 NSFW 检测屏蔽测试
  • Windows 系统中,添加打印机主要有以下几种方式
  • macos使用FFmpeg与SDL解码并播放H.265视频
  • Git常用操作大全(附git操作命令)
  • 【LeetCode】18. 四数之和
  • 微服务的编程测评系统13-我的竞赛列表-elasticSearch
  • javaweb开发笔记—— 前端工程化
  • Spring Boot 集成 Redis 发布订阅实现消息通信
  • 计算机网络技术学习-day6《三层交换机配置》
  • 01 网络信息内容安全--绪论
  • 2025.7.19卡码刷题-回溯算法-组合
  • Web 安全之 HTTP 响应截断攻击详解
  • 数据结构初阶:排序算法(三)归并排序、计数排序
  • 【数据结构】深入解析选择排序与堆排序:从基础到高效实现的完全指南
  • 深度卷积神经网络AlexNet
  • openEuler系统中r如何将docker安装在指定目录
  • 神经网络中 标量求导和向量求导
  • 如何通过传感器选型优化,为设备寿命 “续航”?
  • 机器学习6
  • RootDB:一款开源免费的Web报表工具
  • 0821 sqlite3_get_table函数(数据库函数的补充)
  • Vue.js 中使用 Highcharts 构建响应式图表 - 综合指南
  • 遥感机器学习入门实战教程|Sklearn案例⑤:集成学习方法全览
  • Python学习-- 数据库和MySQL入门