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

【CI1303 离在线】观察者模式解耦

当前项目中的紧耦合问题

通过分析代码,我发现项目中存在严重的模块间紧耦合问题,主要体现在以下几个方面:

1. 串口监听器与状态机之间的紧耦合

在 CISerialListener::HandleProtocolCallback 方法中,直接操作了状态机的标志位:

case WAKE_UP:/*如果是唤醒, 设置m_isWakeUp = true*/ 
{// ...#if (ENABLE_SELF_FSM == 1)MainHandler::getInstance().set_wakeup_flag(true);#endif m_isWakeUp = true;break;
}case VAD_START: /*如果是 VAD Start */
{// ...#if (ENABLE_SELF_FSM == 1)if(!main_handler.is_capturing()){CI_DBG_COUT << "[CISerialListener]状态非 Capturing,上传数据不处理(当前状态:"<<main_handler.fsm.getCurrentState() <<std::endl;break;}#endif // ...break;
}

这种实现方式的问题:

  1. 违反单一职责原则:串口监听器本应只负责监听和解析数据,但现在还承担了控制状态机状态的责任
  2. 高耦合度:串口监听器必须知道状态机的存在及其API
  3. 难以扩展:如果将来需要添加更多的事件处理逻辑,需要不断修改串口监听器的代码
  4. 难以测试:测试串口监听器时需要模拟整个状态机环境

2. 状态机与其他模块的紧耦合

在状态机的各种状态处理函数中,直接调用了串口监听器和其他模块的方法:

// 在状态机处理函数中直接访问外部模块
if (main_handler.isWakeup) {// ...
}// 或者在通信适配器中直接访问串口监听器
bool clsCommunicator::isWakeUp() {return cias::CISerialListener::isWakeUp();
}

使用观察者模式解耦的好处

1. 降低模块间的依赖关系

通过引入观察者模式,各个模块只需要关注自己的职责,而不需要知道其他模块的具体实现:

// 串口监听器只需要发出事件
EventManager::getInstance().notify(EventType::WAKE_UP, eventData);// 状态机只需要订阅感兴趣的事件
EventManager::getInstance().subscribe(EventType::WAKE_UP, this);

2. 提高代码的可维护性和可扩展性

当需要添加新的事件处理逻辑时,只需要添加新的观察者,而不需要修改现有代码。

3. 增强代码的可测试性

每个模块都可以独立测试,只需要模拟事件的发送和接收即可。

具体修改建议

1. 创建事件管理系统

cpp
// events/EventType.hpp
#pragma onceenum class EventType {WAKE_UP,EXIT_WAKE_UP,VAD_START,PCM_MIDDLE,PCM_FINISH,PLAY_DATA_GET,SKIP_INVALID_SPEAK,LOCAL_AUDIO_PLAY_START,LOCAL_AUDIO_PLAY_STOP,CAPTURE_DONE,STATE_CHANGED
};struct EventData {EventType type;std::any payload;std::chrono::steady_clock::time_point timestamp;EventData(EventType t) : type(t), timestamp(std::chrono::steady_clock::now()) {}
};
cpp
 
// events/EventObserver.hpp #pragma once #include "EventType.hpp" #include <any>class EventObserver { public:virtual ~EventObserver() = default;virtual void onEvent(const EventData& event) = 0; };
cpp
 
// events/EventManager.hpp #pragma once #include "EventObserver.hpp" #include <unordered_map> #include <vector> #include <mutex> #include <algorithm>class EventManager { private:EventManager() = default;public:static EventManager& getInstance() {static EventManager instance;return instance;}void subscribe(EventType eventType, EventObserver* observer) {std::lock_guard<std::mutex> lock(mutex_);observers_[eventType].push_back(observer);}void unsubscribe(EventType eventType, EventObserver* observer) {std::lock_guard<std::mutex> lock(mutex_);auto it = observers_.find(eventType);if (it != observers_.end()) {auto& observers = it->second;observers.erase(std::remove(observers.begin(), observers.end(), observer),observers.end());}}void notify(const EventData& event) {std::lock_guard<std::mutex> lock(mutex_);auto it = observers_.find(event.type);if (it != observers_.end()) {for (auto* observer : it->second) {observer->onEvent(event);}}}private:std::unordered_map<EventType, std::vector<EventObserver*>> observers_;std::mutex mutex_; };

2. 修改串口监听器

cpp
 
// ... existing code ... #include "events/EventType.hpp" #include "events/EventManager.hpp"namespace cias { // ... existing code ...
cpp
 
// ... existing code ... void CISerialListener::HandleProtocolCallback(const cias_parse_result_t& parse_result) {auto cmd_type = parse_result.cmd_type;// 创建事件数据EventData eventData(cmd_type);eventData.payload = parse_result;/** * @detail:根据命令类型,执行不同的操作 */switch(cmd_type){case WAKE_UP:{CI_DBG_COUT << "[SerialListener]收到唤醒命令,设置PCM状态为 RCV_PCM_WAKEUP" <<std::endl;if (!pcm_processor_) {pcm_processor_ = std::make_shared<PcmDataCaptator>(mic_audio_fifo_);}pcm_processor_->set_audio_state(cias::RCV_PCM_WAKEUP);// 发布事件而不是直接操作状态机m_isWakeUp = true;EventManager::getInstance().notify(EventData(EventType::WAKE_UP));break;}case VAD_START:{CI_DBG_COUT << "[SerialListener]收到VAD开始命令,准备接收PCM数据" <<std::endl;if (!pcm_processor_) {pcm_processor_ = std::make_shared<PcmDataCaptator>(mic_audio_fifo_);}pcm_processor_->set_audio_state(cias::RCV_PCM_START);#if (ENABLE_SELF_PIPE == 1)#if (PCM_CAPTATOR_THREAD_VERSION_ == PCM_CAPTATOR_THREAD_VERSION_)pcm_processor_->start_processing_thread();#elsepcm_processor_->notify_start_processing();#endif#endif // 发布事件EventManager::getInstance().notify(EventData(EventType::VAD_START));break;}case PCM_FINISH:{if (pcm_processor_) {pcm_processor_->set_audio_state(cias::RCV_PCM_FINISH);pcm_processor_->set_pcm_end(true);#if (ENABLE_SELF_PIPE == 1)#if (PCM_CAPTATOR_THREAD_VERSION_ == PCM_CAPTATOR_THREAD_VERSION_)pcm_processor_->stop_processing_thread();#elsepcm_processor_->stop_processing();#endif#endif}// 发布事件EventManager::getInstance().notify(EventData(EventType::PCM_FINISH));break;}// ... 其他case类似处理 ...default:pcm_processor_->set_audio_state(cias::RCV_PCM_IDLE);break;} } // ... existing code ...

3. 修改状态机使其成为事件观察者

cpp
 
// ... existing code ... #include "events/EventObserver.hpp" #include "events/EventManager.hpp"class MainHandler : public EventObserver { private:MainHandler() {// 订阅感兴趣的事件EventManager::getInstance().subscribe(EventType::WAKE_UP, this);EventManager::getInstance().subscribe(EventType::VAD_START, this);EventManager::getInstance().subscribe(EventType::PCM_FINISH, this);// ... 订阅其他事件 ...}public:// 实现EventObserver接口void onEvent(const EventData& event) override {switch(event.type) {case EventType::WAKE_UP:isWakeup = true;break;case EventType::VAD_START:// 可以添加额外的处理逻辑break;case EventType::PCM_FINISH:isCapturedDone = true;break;// ... 处理其他事件 ...}}// ... existing code ... };

4. 修改通信适配器

cpp
 
// ... existing code ... #include "events/EventObserver.hpp" #include "events/EventManager.hpp"class clsCommunicator : public clsThread, public EventObserver { public:clsCommunicator(std::shared_ptr<cias::PcmDataCaptator> vcmAudioIn, MP3SerialSender &vcmSender):pAudioIn(vcmAudioIn),m_vcmSender(vcmSender){ // 订阅事件EventManager::getInstance().subscribe(EventType::WAKE_UP, this);EventManager::getInstance().subscribe(EventType::PCM_FINISH, this);}// 实现EventObserver接口void onEvent(const EventData& event) override {switch(event.type) {case EventType::WAKE_UP:// 处理唤醒事件break;case EventType::PCM_FINISH:// 处理PCM完成事件break;}}// ... existing code ... };

总结

通过引入观察者模式,我们可以获得以下好处:

  1. 降低耦合度:模块之间不再直接依赖,而是通过事件进行通信
  2. 提高可维护性:修改一个模块不会影响其他模块
  3. 增强可扩展性:添加新功能只需添加新的观察者
  4. 改善可测试性:每个模块可以独立测试
  5. 符合开闭原则:对扩展开放,对修改关闭

这样的重构将使代码更加清晰、易于维护和扩展,同时提高了系统的灵活性和可测试性。

http://www.rkmt.cn/news/83846.html

相关文章:

  • 数组int [ ]ints=new int[ ] { };
  • 计算机毕业设计springboot农贸市场管理系统 SpringBoot 智慧农批市场综合运营平台 SpringBoot 农产品集散中心数字管理系统
  • RPA 重塑 IT 运维:6 大核心场景解锁自动化新效能
  • day36 阅读官方文档
  • next-ai-draw-io
  • [Windows] 表白程序生成工具 v1.0
  • 终极DoublePulsar检测指南:5分钟快速发现系统后门威胁
  • JAVA安装教程 (windows版),入门第一项,小白收藏这篇就够了
  • 为什么比话能把论文的ai率降低下来?2025年降ai选比话靠谱吗?
  • GitHub访问慢、无法访问解决
  • 闫俊杰罗永浩播客访谈观点汇总 - -Watcher
  • [大模型] 解读腾讯混元大模型
  • 【稀缺技术揭秘】:微软工程师不愿公开的Azure量子作业调试技巧(基于VSCode)
  • 小红的矩阵【牛客tracker 每日一题】
  • 寫代碼總是最簡單的
  • 系统编程之进程
  • 利用 PHPStudy(Mac 版)部署 Nuxt3 node-server 模式项目完整教程
  • 负载均衡-LVS 全解析
  • DAY23常见聚类算法
  • 晶体塑性有限元显示动力学cpfem_vumat子程序(界面调用程序)
  • Wan2.2-T2V-A14B生成动画短片全流程实录
  • Vite 如何优化项目的图片体积
  • Java Web 养老院管理系统系统源码-SpringBoot2+Vue3+MyBatis-Plus+MySQL8.0【含文档】
  • 如有可能,你應該和本就幸福的人結婚
  • 前端岗来了个男生,没两天就被劝退了
  • Spring Boot + Kafka 实战:从入门到避坑,小白也能轻松上手!
  • 活在時光裏的父母
  • 【专家亲授】C++26与传统头文件协同工作:企业级编译架构设计
  • 人工智能之数学基础 线性代数:第一章 向量与矩阵
  • 53