【设计模式】观察者模式解耦
当前项目中的紧耦合问题
通过分析代码,我发现项目中存在严重的模块间紧耦合问题,主要体现在以下几个方面:
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;
}
这种实现方式的问题:
- 违反单一职责原则:串口监听器本应只负责监听和解析数据,但现在还承担了控制状态机状态的责任
- 高耦合度:串口监听器必须知道状态机的存在及其API
- 难以扩展:如果将来需要添加更多的事件处理逻辑,需要不断修改串口监听器的代码
- 难以测试:测试串口监听器时需要模拟整个状态机环境
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 once
enum 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();
#else
pcm_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();
#else
pcm_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 ...
};
总结
通过引入观察者模式,我们可以获得以下好处:
- 降低耦合度:模块之间不再直接依赖,而是通过事件进行通信
- 提高可维护性:修改一个模块不会影响其他模块
- 增强可扩展性:添加新功能只需添加新的观察者
- 改善可测试性:每个模块可以独立测试
- 符合开闭原则:对扩展开放,对修改关闭
这样的重构将使代码更加清晰、易于维护和扩展,同时提高了系统的灵活性和可测试性。

浙公网安备 33010602011771号