DAB C++ 版本设计模式应用实践
1. 命令模式 (Command Pattern)
设计目标
- 模块解耦:实现各模块独立编译、测试、运行,消除模块间直接依赖
- 扩展准备:为桥接模式实现奠定基础
- 依赖倒置:通过命令对象反转模块依赖方向
- 耦合降低:将模块间耦合简化为命令对象耦合
1.1 MQTT 模块实现
#pragma once #include <string> #include <vector> #include <functional> /** * @class HiMqttClient * @brief MQTT 客户端核心类,提供连接管理、消息发布订阅等功能 * * @note 采用命令模式实现消息处理回调机制 */ class HiMqttClient { public: // 连接管理接口 static void start(const char* ip, int port, const char* user, const char* password, const char* clientId); static void stop(); // 消息管理接口 static void subscribe(const std::vector<std::string>& topics); static bool publish(const char* topic, const char* body); // 命令模式回调设置 static void onTopic(const std::function<void(const char*, const char*, const char*, const char*)>& func); };
设计亮点:
- 通过
onTopic()设置命令对象实现消息处理解耦 - 支持模块独立单元测试
- 消除与其他业务模块的循环依赖
1.2 Topic 处理器实现
#pragma once #include <map> #include <functional> #include "context/dab_context.h" /** * @class TopicHandler * @brief 基于命令模式的主题处理器 * * @note 使用注册机制替代传统 switch-case 分支处理 */ class TopicHandler { public: // 生命周期管理 static void init(int maxThreads); static void destroy(); // 命令注册接口 static void registerHandler(const std::map<std::string, std::function<void(DABContext&)>>& handles); // 统一消息入口 static void onTopic(const char* topic, const char* body, const char* response_topic, const char* correlation_data); };
创新点:
- 动态注册机制实现处理逻辑可配置化
- 统一消息入口简化调用链路
- 天然支持多线程处理
1.3 上下文对象设计
#pragma once #include "context/dab_status.h" #include "context/dab_request.h" #include "context/dab_response.h" /** * @class DABContext * @brief 请求处理上下文对象 * * @note 采用命令模式封装消息发布功能 */ class DABContext { public: // 状态管理接口 bool is_ok() const; void success(); void clientFail(const char* out_log, const char* inner_log = nullptr); // 消息发布命令接口 static void setPublishFunc(const std::function<void(const char*, const char*)>& func); static void publish(DABContext& context); // 数据成员 DABStatus status; DABRequest request; DABResponse response; };
技术优势:
- 隐藏 MQTT 实现细节
- 支持多种消息发布策略
- 上下文自包含设计简化单元测试
1.4 日志模块实现
#pragma once #include <functional> /** * @enum DABLogLevel * @brief 日志级别枚举定义 */ enum class DABLogLevel { INFO, WARNING, ERROR, FATAL, ALWAYS }; namespace dab { /** * @brief 日志回调设置接口 * @param callback 日志处理函数原型: * void(日志级别, 文件名, 函数名, 行号, 日志内容) */ void set_log_callback(std::function<void(DABLogLevel, const char*, const char*, int, const char*)> callback); // 日志宏定义 #define DABLOG_INFO(...) dab::writeLog(DABLogLevel::INFO, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__) #define DAB_ASSERT(expr, desc) do { if (!(expr)) { dab::on_assert_fail(__FILE__, __FUNCTION__, __LINE__); }} while(0) }
核心价值:
- 灵活适配不同日志实现
- 支持运行时日志策略切换
- 提供丰富的调试信息
2. 桥接模式 (Bridge Pattern)
设计目标
- 架构解耦:分离抽象与具体实现
- 独立演进:各模块可独立变化
- 统一接口:提供标准化服务能力
系统桥接实现
void publish(const char* topic, const char* body) { HiMqttClient::publish(topic, body); } extern "C" int runDabService() { // 初始化各模块 TopicHandler::init(DABProperties::max_handle_threads); DABHandler::init(); // 桥接关键点 HiMqttClient::onTopic(TopicHandler::onTopic); DABContext::setPublishFunc(publish); // 订阅与注册 HiMqttClient::subscribe(DABHandler::getTopics()); TopicHandler::registerHandler(DABHandler::getTopicHandles()); // 启动服务 HiMqttClient::start(DABProperties::mqtt_ip.c_str(), DABProperties::mqtt_port, ...); return 0; }
桥接优势:
- 业务逻辑与通信协议解耦
- 模块间通过抽象接口通信
- 新增协议支持成本最低化
3. 适配器模式 (Adapter Pattern)
设计目标
- 接口标准化:统一不同系统的接口规范
- 依赖倒置:反转系统接口依赖方向
- 扩展支持:为策略模式实施奠定基础
典型应用
- 日志适配器:将系统日志接口转换为 DAB 标准日志接口
- 服务适配:封装平台特性接口为统一服务接口
适配收益:
- 业务代码不依赖具体实现
- 新增平台支持只需实现适配器
- 保持核心逻辑稳定性
4. 策略模式 (Strategy Pattern)
应用场景
| 场景 | 实现策略 | 优势 |
|---|---|---|
| 电视环境 | 真实硬件接口实现 | 完整功能支持 |
| 云端环境 | Mock 接口实现 | 无硬件依赖的自动化测试 |
| 单元测试 | 内存型轻量实现 | 快速测试执行 |
策略配置
// 测试环境初始化示例 void dabInit() { DABContext::setPublishFunc([](const char* t, const char* b) { DABLOG_ALWAYS("Test Publish: %s -> %s", t, b); }); TopicHandler::registerHandler({ {"test/topic1", [](DABContext& ctx){ /* Mock处理逻辑 */ }}, {"test/topic2", [](DABContext& ctx){ /* Mock处理逻辑 */ }} }); }
策略优势:
- 运行时动态切换实现
- 环境隔离保证测试可靠性
- 并行支持多种部署方案
5.单例模式
在DAB的实现中,有意回避了单例模式,使用静态类代替。 ```cpp class TopicHandler { public: static void init(int maxThreads); // 显式初始化 static void destroy(); // 显式资源释放 static void registerHandler(/*...*/); TopicHandler() = delete; // 禁止实例化 }; ``` **设计考量**: 1. **生命周期可控**:通过`init()`/`destroy()`明确管理资源 2. **测试友好**:支持不同测试用例的独立初始化 3. **避免全局状态**:每个模块维护自身静态数据 4. **编译期约束**:`= delete`禁止非法操作 **对比传统单例**: * 不强制全局唯一实例 * 无隐式初始化顺序问题 * 支持多环境配置(测试/生产)
6. 测试体系设计
1. 测试环境搭建
#include <gtest/gtest.h> #include "dab/dab_api.h" // 全局测试环境类 class TestEnv : public testing::Environment { public: void SetUp() override { dabInit(); } // 测试用例初始化 void TearDown() override { dabDestory(); } // 测试资源回收 }; // 核心初始化逻辑 namespace { // 模拟消息发布函数 void publish(const char* topic, const char* body) { DABLOG_ALWAYS("[TEST] Topic:%snPayload:%s", topic, body); } void dabInit() { DABProperties::device_id = "TEST_DEVICE"; // 设置测试设备ID TopicHandler::init(5); // 初始化消息处理线程池 DABHandler::init(); // 业务处理器初始化 DABContext::setPublishFunc(publish); // 注入模拟发布器 // 注册Topic处理函数 TopicHandler::registerHandler(DABHandler::getTopicHandles()); } void dabDestory() { TopicHandler::destroy(); // 清理消息处理器 DABHandler::destroy(); // 清理业务处理器 } } // 测试主入口 int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); testing::AddGlobalTestEnvironment(new TestEnv); return RUN_ALL_TESTS(); }
2. 设计模式应用解析
| 设计模式 | 应用场景 | 实现要点 |
|---|---|---|
| 命令模式 | 消息处理函数注册 | 通过registerHandler注册处理闭包 |
| 桥接模式 | 业务处理与MQTT通信解耦 | setPublishFunc实现协议隔离 |
| 策略模式 | 测试环境与生产环境配置切换 | DABProperties动态配置 |
3.测试辅助工具实现
1. Topic处理工具类
namespace { // 生成标准Topic格式 std::string formatTopic(const std::string& baseTopic) { std::ostringstream oss; oss << "dab/" << DABProperties::device_id << "/" << baseTopic; return oss.str(); } } // 统一测试入口函数 void onTopicTest(const std::string& topic, const std::string& request) { TopicHandler::onTopic( formatTopic(topic).c_str(), // 格式化请求Topic request.c_str(), // 测试请求载荷 "_response/" + formatTopic(topic).c_str(), // 响应Topic "" // 关联数据 ); }
2. 工具类设计亮点
- Topic规范化:自动添加设备ID前缀
- 响应隔离:生成专用的响应Topic通道
- 异常防护:内置空指针检查等安全机制
- 日志追踪:自动记录测试消息流向
4.应用功能测试案例
1. 应用管理测试集
TEST(DABHandler, ApplicationManagement) { // 基础功能测试 onTopicTest("applications/list", "{}"); // 空参数查询 // 典型场景测试 onTopicTest("applications/launch", R"({"appId":"YouTube"})"); // 标准应用启动 // 带参数启动测试 onTopicTest("applications/launch", R"({ "appId": "Netflix", "parameters": [ "-KEY", "https%3A%2F%2Fwww.example.com%2F", "-STANDALONE_PARAM" ] })"); // 状态管理测试 onTopicTest("applications/get-state", R"({"appId":"YouTube"})"); // 退出机制测试 onTopicTest("applications/exit", R"({"appId":"YouTube"})"); // 常规退出 onTopicTest("applications/exit", R"({"appId":"YouTube","background":true})"); // 后台退出 }