首页 > 编程笔记 > C++笔记 阅读:1

C++状态模式及其实现(非常详细)

状态模式是一种用于解决对象状态转换导致的复杂性问题行为模式。

在状态模式中,对象的行为依赖于其内部状态,并根据其当前状态来调用不同的方法。通过将每种状态封装在一个单独的类中,状态模式使状态转换更加直观且易于扩展。

状态模式的主要作用是将复杂的条件判断逻辑转移到表示不同状态的类中,从而简化了对象行为的管理。

通过状态模式可以将复杂的条件判断逻辑封装在具体状态类中,使代码更加清晰且易于扩展。当需要添加新的状态时,只需创建一个新的具体状态类并实现相应的行为,而无须修改环境类或其他的具体状态类。

状态模式简化了对象行为的管理,将复杂的条件判断逻辑转移到状态类中;对象的状态转换变得更加直观,易于理解和扩展;对开闭原则的支持,可以在不修改环境类的情况下增加新的状态。

然而,状态模式容易造成类或者方法的数量大幅度增加,可能会导致代码的复杂性增加。此外,如果状态之间的转换逻辑太复杂,则可能需要使用其他模式来处理状态之间的关系。

传统状态模式

传统状态模式主要包括环境类(Context)、抽象状态类(State)和具体状态类(Concrete State),如下图所示。


图 1 传统状态模式UML简图

1) 环境类包含了一种状态对象作为其成员变量,并提供了一些方法来根据当前状态调用不同的行为。环境类负责维护状态对象的切换,并委托给状态对象来执行相应的行为。

2) 抽象状态类定义了状态对象的接口,并声明了一些方法,用于处理特定状态下的行为。它可以有多个具体状态类与之对应。

3) 具体状态类是抽象状态类的子类,实现了特定状态下的行为。每个具体状态类对应于对象的一种状态,并负责在特定状态下执行相应的行为。

C++11元编程下的结构设计

状态模式组件主要包括状态事件、状态节点和转换路径:

图 2 C++11模板状态模式UML简图

stStat 是状态节点的定义;stArc 是转换路径;state 是状态模式模板类。concreteState 是具体状态模式,这里只需针对 state 模板类型进行特化处理。

整个模块使用元编程技术实现,状态描述数据类型和条件转换数据类型都是用模板参数描述的。

C++状态模式实现和解析

状态模式的实现中使用了中介者模式,利用中介者模式传递各种状态的变化消息。代码主要分成事件定义、状态节点和转换路径定义、节点和转换路径管理、状态转换管理。

在 private__ 中定义供模块内部使用的数据和方法,这些内容保护在模块内部不对外释放接口。状态转换过程中的事件分成 4 种,即状态机就绪、状态机运行结束、进入状态和离开状态,代码如下:
//designM/state.hpp
#pragma once
#include<type_traits >
#include<functional >
#include<memory >
#include<map >
#include<vector >

namespace private__
{
    //这是状态变化事件类型定义
    enum class emEvent
    {
        EVT_ENT,    //进入状态触发事件
        EVT_LEAVE,  //离开状态触发事件
        EVT_READY,  //模块就绪事件
        EVT_END     //运行到结束节点的事件
    };

对于状态节点定义,模板参数是状态标记类型。方便自定义需要的类型,例如分析网络协议可以使用 uint8_t,以及定义路灯状态可以使用自己定义的枚举类型,代码如下:
template< typename STATE_TYPE >
struct stStat
{
    STATE_TYPE m_state;
    stStat(const STATE_TYPE&s):m_state(s){}
};
//状态转换关系节点定义
template< typename STATE_TYPE,typename CONDITION_DATA_TYPE >
struct stArc
{
    STATE_TYPE m_from;    //来源状态
    STATE_TYPE m_to;      //目标状态

对于转换条件处理函数,如果转换条件满足,则返回值为 true。这个函数首先通过输入数据激励,然后判断是否需要进行状态变化,如果函数的返回值为 true,则执行状态转换,代码如下:
        std::function<bool(const CONDITION_DATA_TYPE&)> m_condition;
        stArc(const STATE_TYPE& from, const STATE_TYPE& to, std::function<bool(const CONDITION_DATA_TYPE&)> fun):m_from(from), m_to(to), m_condition(fun) {}
    };
}

template<typename STATE_TYPE, typename CONDITION_DATA_TYPE>
class state {
public:
    static_assert(std::is_arithmetic<CONDITION_DATA_TYPE>::value ||
                  (std::is_class<CONDITION_DATA_TYPE>::value &&
                   std::is_default_constructible<CONDITION_DATA_TYPE>::value),
                  "");

    using evtType_t = private__::emEvent;
    using stateData_t = typename std::remove_pointer<typename std::decay<STATE_TYPE>::type>::type;
    using state_t = private__::stStat<stateData_t>;
    using arc_t = private__::stArc<stateData_t, CONDITION_DATA_TYPE>;

用 map 保存节点的转换关系,stateData_t 是源节点,作为检索的 key,因为一个源节点可能有多个目标节点状态使用 std::vector<stateData_t>保存目标节点,代码如下:
    using arcData_t = std::map< stateData_t,std::vector< arc_t >>;
protected:

使用中介者模式传递消息,参与者分为消息的生产者和消费者。生产者发出状态变化的 4 种消息。接收者进行响应处理并对外调用回调函数对具体事务进行处理,代码如下:
class colleague;
using mediator_t = mediator< colleague,
               stateData_t,
                 private__::emEvent,
                 CONDITION_DATA_TYPE >;

//定义消息接口
class colleague:public colleagueItfc<
    mediator_t,stateData_t,
    private__::emEvent,
    CONDITION_DATA_TYPE >
{
    public:
        std::function< void
        (stateData_t,
             private__::emEvent,CONDITION_DATA_TYPE) > m_cb__;
public:
    colleague(std::shared_ptr<mediator_t > m):
        colleagueItfc< mediator_t,stateData_t,
    private__::emEvent,CONDITION_DATA_TYPE >(m){}
    virtual~colleague(){}

实现消息接收接口。消息传递使用了 std::tuple,这里需要根据具体应用将 tuple 内容解出来,代码如下:
        virtual void recv(const std::tuple<stateData_t,
                             private__::emEvent,
                             CONDITION_DATA_TYPE> tpl) final
        {
            if (m_cb__) {               // 调用实际的处理函数
                m_cb__(std::get<0>(tpl),
                       std::get<1>(tpl),
                       std::get<2>(tpl));
            }
        }
    };
    // 状态节点表类型
    using stateTbl_t = std::map<stateData_t, private__::stStat<stateData_t>>;

protected:
    stateTbl_t            m_stats__;   // 状态节点表
    arcData_t             m_arcs__;    // 状态连接弧表
    stateData_t           m_current__; // 当前状态
    stateData_t           m_start__;   // 初始状态
    stateData_t           m_end__;     // 结束状态
    std::atomic<bool>     m_is_running__; // 状态机运行标志
    std::shared_ptr<mediator_t> pt_mdt__; // 中介者模式的消息处理器

将中介者模式分为生产者和消费者两个实例,生产者处理状态转换并发送事件。消费者处理事件,通过回调函数将消息发送给外部程序。在状态转换前发送 EVT_LEAVE 事件,转换完成后进入新的状态并发送 EVT_ENT 事件,代码如下:
    std::shared_ptr< colleague > pt_producer__; //事件数据的生产者
    std::shared_ptr< colleague > pt_consumer__; //事件数据的消费者
protected:

状态离开和进入时发送消息通知的两种方法。这两种方法都是私有的,在内部进行状态转换时调用,代码如下:
   void call_leave__(const stateData_t&state,
           const CONDITION_DATA_TYPE&data){
       pt_producer__->sendTo(
       pt_consumer__,
       state,
       private__::emEvent::EVT_LEAVE,data);
   }

   void call_ent__(const stateData_t&state,
                  const CONDITION_DATA_TYPE&data)
   {
       pt_producer__->sendTo(
          pt_consumer__,
          state,
          private__::emEvent::EVT_ENT,data);
   }
public:

在模块的构造函数中准备中介者模式进行中间消息传递操作,构造中介者模式对象并添加消息的生产者和消费者两个角色,代码如下:
state():m_is_running__(false)
{
    pt_mdt__ = std::make_shared<mediator_t >();

    pt_producer__ = std::make_shared<colleague >(pt_mdt__);
    pt_consumer__ = std::make_shared<colleague >(pt_mdt__);

    pt_mdt__->add(pt_producer__);
    pt_mdt__->add(pt_consumer__);
}

virtual~state()
{
    //结束中介者模式
    if(pt_mdt__){
        pt_mdt__->run(false);
    }
}

函数 on() 用来指定业务程序中事件的响应方法,在这种方法中接收事件发生的状态参数、事件参数和转换激励数据。在这种方法中配合策略模式进行处理可以很灵活地处理不同状态,这种方式在后面的示例中也可以看到,代码如下:
void on(std::function<void(stateData_t,
       private__::emEvent,CONDITION_DATA_TYPE) > fun)
{
    pt_consumer__->m_cb__ = fun;
}

函数 setStart() 和 setEnd() 方法用来指定状态机的起始状态和终止状态,代码如下:
void setStart(const stateData_t&data)
{
    m_start__ = data;
}
void setEnd(const stateData_t&data)
{
    m_end__ = data;
}

函数 start() 用来启动或者停止状态机,启动时同时启动中介者模式的后台线程。启动后首先从开始状态启动,并且发送 EVT_START 事件,实现代码如下:
void start(bool sw)
{
   if(m_is_running__ == sw){return;}

   m_is_running__ = sw;
   if(sw){
       //启动中介者模式后台
       pt_mdt__->run(true);
       m_current__ = m_start__;
       //发送就绪通知
       pt_producer__->sendTo(
       pt_consumer__,
       m_start__,
       private__::emEvent::EVT_READY,
       std::is_arithmetic<CONDITION_DATA_TYPE >::value?
            CONDITION_DATA_TYPE():(CONDITION_DATA_TYPE)0.0
   );
   }else{//结束运行
       pt_mdt__->run(false);
   }
}
两个 execute() 方法用来执行状态转换操作。无参数的版本用来执行一次自动的状态转换操作。有参数的版本用来执行一次有条件的转换操作。

执行转换时首先发出离开事件通知,然后改变状态,完成后发送进入新状态的事件通知。如果进入的目标状态是终止状态,则发送 EVT_END 事件通知,代码如下:
bool execute()
{
    if (m_is_running__ == false) return false;
    auto it = m_arcs__.find(m_current__);
    if (it == m_arcs__.end()) return false;
    if (it->second.size() == 0) return false;
    auto item = it->second[0];

    // 这个函数是没有参数条件的转换,主要针对自动转换的情况
    // 先调用离开通知
    call_leave__(item.m_from,
        std::is_arithmetic<CONDITION_DATA_TYPE>::value ?
        CONDITION_DATA_TYPE() : (CONDITION_DATA_TYPE)0.0);

    m_current__ = item.m_to;

    // 再调用进入通知
    call_enter__(item.m_to,
        std::is_arithmetic<CONDITION_DATA_TYPE>::value ?
        CONDITION_DATA_TYPE() : (CONDITION_DATA_TYPE)0.0);

    // 执行结束通知
    if (item.m_to == m_end__) {
        recv(std::make_tuple(
            m_end__,
            private__::emEvent::EVT_END,
            std::is_arithmetic<CONDITION_DATA_TYPE>::value ?
            CONDITION_DATA_TYPE() : (CONDITION_DATA_TYPE)0.0));
    }
    return true;
}

bool execute(const CONDITION_DATA_TYPE& data)
{
    if (m_is_running__ == false) return false;
    // 查找满足起始节点条件的弧表
    auto it = m_arcs__.find(m_current__);
    if (it == m_arcs__.end()) return false;
}

遍历所有的弧找到第 1 个满足条件的弧执行转换操作。这里需要特别注意的是在一次转换操作中必须保证只能有一条满足转换条件的路径,否则第 2 条路径将永远不会访问,并且这样不能满足明确转换目标,代码如下:
for(auto item:it->second){
    if(!item.m_condition(data)){continue;}
    //通知离开起始节点和到达目标节点
    call_leave__(item.m_from,data);
    //切换当前状态
    m_current__ = item.m_to;
    //通知到达目标节点
    call_ent__(item.m_to,data);
    //执行结束通知
    if(item.m_to == m_end__){
        pt_producer__->sendTo(pt_consumer__,m_end__,
            private__::emEvent::EVT_END,
          std::is_arithmetic<CONDITION_DATA_TYPE >::value?
          CONDITION_DATA_TYPE():(CONDITION_DATA_TYPE)0.0);
       }

       break;
   }
   return true;
}

addState() 方法和 removeState() 方法用于添加和移除状态节点操作,代码如下:
bool addState(const stateData_t&state)
{
    if(m_is_running__ == true)return false;
    private__::stStat<stateData_t > s(state);
    auto rst = m_stats__.insert(std::make_pair(state,s));
    return rst.second;
}

void removeState(const stateData_t&state)
{
    if(m_is_running__ == true)return;
    auto it = std::find(m_stats__.begin(),m_stats__.end(),state);
    m_stats__.erase(it);

    //清理状态起始ARC
    auto it1 = m_arcs__.find(state);
    if(it1.m_arcs__.end()){
        m_arcs__.erase(it1);
    }

    //清理状态到达的ARC
    for(auto it2 = m_arcs__.begin();it2! = m_arcs__.end();it2 ++){
        for(auto it3 = it2->second.begin();
                 it3! = it2->second.end();
                 it3 ++){
             if(it3->m_to == state){
                 it2->second.erase(it3);
             }
        }
    }
}

addArc() 方法和 removeArc() 方法用来添加和删除状态转换路径,代码如下:
    void addArc(const stateData_t& from,
                const stateData_t& to,
                std::function<bool(const CONDITION_DATA_TYPE&)> cnd)
    {
        if (m_is_running__ == true) return;
        auto it = m_arcs__.find(from);

        if (it != m_arcs__.end()) {
            private__::stArc<STATE_TYPE, CONDITION_DATA_TYPE> item(from, to, cnd);
            it->second.push_back(item);
        } else {
            private__::stArc<STATE_TYPE, CONDITION_DATA_TYPE> item(from, to, cnd);
            std::vector<private__::stArc<STATE_TYPE, CONDITION_DATA_TYPE>> set{item};
            m_arcs__.insert(std::make_pair(from, set));
        }
    }

    void removeArc(const stateData_t& from, const stateData_t& to)
    {
        if (m_is_running__ == true) return;
        auto it = m_arcs__.find(from);
        if (it != m_arcs__.end()) {
            for (auto itl = it->second.begin(); it != it->end(); it++) {
                if (itl->m_to == to) {
                    it->second.erase(itl);
                }
            }
        }
    }
};

C++状态模式应用示例

这里给出了两种状态模式的示例程序:
代码如下:
//state.cpp
#include <iostream>
#include <thread>
#include "designM/state.hpp"

using namespace wheels;
using namespace dm;

// 定义状态数据
enum class MyState {
    STATE_A,
    STATE_B,
    STATE_C
};

int main() {
    using fsm_t = wheels::dm::state<MyState, int>;
    using event_t = fsm_t::evtType_t;
    fsm_t fsm;

    // 添加状态
    fsm.addState(MyState::STATE_A);
    fsm.addState(MyState::STATE_B);
    fsm.addState(MyState::STATE_C);

    // 添加状态转换
    fsm.addArc(MyState::STATE_A, MyState::STATE_B,
               [](const int& condition) { return condition > 0; });
    fsm.addArc(MyState::STATE_B, MyState::STATE_C,
               [](const int& condition) { return condition > 1; });
    fsm.addArc(MyState::STATE_C, MyState::STATE_A,
               [](const int& condition) { return condition > 2; });

    // 设置起始和结束状态
    fsm.setStart(MyState::STATE_A);
    fsm.setEnd(MyState::STATE_C);

    // 注册状态转换事件响应方法
    fsm.on([](MyState state, event_t event, int condition) {
        switch (event) {
        case event_t::EVT_ENT:
            std::cout << "EVT_ENT ";
            break;
        case event_t::EVT_LEAVE:
            std::cout << "EVT_LEAVE ";
            break;
        case event_t::EVT_READY:   // 模块就绪事件
            std::cout << "EVT_READY ";
            break;
        case event_t::EVT_END:
            std::cout << "EVT_END ";
            break;
        }

        // 处理不同的状态
        switch (state) {
        case MyState::STATE_A:
            std::cout << "state A\n";
            break;
        case MyState::STATE_B:
            std::cout << "state B\n";
            break;
        case MyState::STATE_C:
            std::cout << "state C\n";
            break;
        }
    });

    // 启动状态机
    fsm.start(true);

    // 执行状态转换
    fsm.execute(1);
    fsm.execute(2);
    fsm.execute(3);

    std::this_thread::sleep_for(std::chrono::seconds(1));
    fsm.start(false);

    return 0;
}
上面示例的运行结果如下:

EVT_READY State A
EVT_LEAVE State A
EVT_ENT State B
EVT_LEAVE State B
EVT_ENT State C
EVT_END State C
EVT_LEAVE State C
EVT_ENT State A


下面的示例利用策略模式配合完成一个解耦性更好的状态模式,代码如下:
// state2.cpp
#include <iostream>
#include <functional>
#include "designM/state.hpp"
#include "designM/strategy.hpp"

using namespace wheels;
using namespace dm;

// 状态类型
enum class MyState {
    STATE_Start,
    STATE_A,
    STATE_B,
    STATE_C,
    STATE_End
};

// 定义状态转换激励数据
const int StartToA = 0;
const int AToB     = 1;
const int BToC     = 2;
const int CToEnd   = 3;

int main() {
    // 声明状态机和策略模式类型
    using fsm_t      = state<MyState, int>;
    using event_t    = fsm_t::evtType_t;
    using strategy_t = strategy<MyState,
                                std::function<void(MyState, event_t, int)>>;

    // 实例化对象
    fsm_t        fsm;
    strategy_t   branch_caller;

    // 为各状态注册事件处理
    branch_caller.add(MyState::STATE_Start,
        [](MyState state, event_t event, int data) {
            std::cout << "STATE_Start: ";
            if (event == event_t::EVT_ENT) { }
        });

    branch_caller.add(MyState::STATE_A,
        [&](MyState state, event_t event, int data) {
            std::cout << "STATE_A: ";
            if (event == event_t::EVT_ENT) {
                std::cout << "(DoSomeThing)";
                fsm.execute(AToB);
            }
        });

    branch_caller.add(MyState::STATE_B,
        [&](MyState state, event_t event, int data) {
            std::cout << "STATE_B: ";
            if (event == event_t::EVT_ENT) {
                std::cout << "(DoSomeThing)";
                fsm.execute(BToC);
            }
        });

    branch_caller.add(MyState::STATE_C,
        [&](MyState state, event_t event, int data) {
            std::cout << "STATE_C: ";
            if (event == event_t::EVT_ENT) {
                std::cout << "(DoSomeThing)";
                fsm.execute(CToEnd);
            }
        });

    branch_caller.add(MyState::STATE_End,
        [&](MyState state, event_t event, int data) {
            std::cout << "STATE_End: ";
        });

    // 添加状态
    fsm.addState(MyState::STATE_Start);
    fsm.addState(MyState::STATE_A);
    fsm.addState(MyState::STATE_B);
    fsm.addState(MyState::STATE_C);
    fsm.addState(MyState::STATE_End);

    // 添加状态转换
    fsm.addArc(MyState::STATE_Start, MyState::STATE_A,
               [](const int& condition) { return condition == StartToA; });
    fsm.addArc(MyState::STATE_A, MyState::STATE_B,
               [](const int& condition) { return condition == AToB; });
    fsm.addArc(MyState::STATE_B, MyState::STATE_C,
               [](const int& condition) { return condition == BToC; });
    fsm.addArc(MyState::STATE_C, MyState::STATE_End,
               [](const int& condition) { return condition == CToEnd; });

    // 设置起始与结束状态
    fsm.setStart(MyState::STATE_Start);
    fsm.setEnd(MyState::STATE_End);

    // 注册状态机事件回调
    fsm.on([&](MyState state, event_t event, int condition) {
        branch_caller.call(state, state, event, condition);
        switch (event) {
        case event_t::EVT_ENT:
            std::cout << "EVT_ENT\n";
            break;
        case event_t::EVT_LEAVE:
            std::cout << "EVT_LEAVE\n";
            break;
        case event_t::EVT_READY:
            std::cout << "EVT_READY\n";
            break;
        case event_t::EVT_END:
            std::cout << "EVT_END\n";
            break;
        }
    });

    // 启动并执行状态机
    fsm.start(true);
    fsm.execute(StartToA);
    std::this_thread::sleep_for(std::chrono::seconds(1));
    fsm.start(false);

    return 0;
}
上述示例程序的执行结果如下:

STATE_Start:EVT_READY
STATE_Start:EVT_LEAVE
STATE_A:(DoSomeThing)EVT_ENT
STATE_A:EVT_LEAVE
State B:(DoSomeThing)EVT_ENT
State B:EVT_LEAVE
State C:(DoSomeThing)EVT_ENT
State C:EVT_LEAVE
STATE_End:EVT_ENT
STATE_End:EVT_END

相关文章