【设计模式】观察者模式解耦

当前项目中的紧耦合问题

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

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

总结

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

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

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

posted @ 2025-12-11 23:51  FBshark  阅读(5)  评论(0)    收藏  举报