美文网首页Cocos2d游戏开发技巧
[因为我不懂啊]-什么是状态机编程(设计模式)(2)

[因为我不懂啊]-什么是状态机编程(设计模式)(2)

作者: qufl | 来源:发表于2016-09-19 03:12 被阅读3332次

    先提一下昨天的思路:

    • 电梯包含了四种状态:关门,开门,运动,停止
    • 状态之间会进行转换:关门(默认)->开门->关门->运动->停止->开门->关门

    所以,在代码实现时,就出现了一个[状态(枚举)][电梯(类)],其中[电梯]包含了四种状态的处理逻辑,以及提供了一个[去某一层]的方法。状态之间的转换直接写在状态的处理逻辑中(例如开门状态的处理,就是转换到关门状态)。

    然后,以此为基础,再进行今天的状态机的学习。


    经过一番查阅,我对状态机的理解有一些改变了:
    从电梯的例子来看,电梯的运行过程包含了4个状态。如果我在电梯的gotoFloor函数中,使用
    if (当前为关门状态) { 关门状态的处理;下一个状态; } else if (当前为开门状态) {开门状态的处理;下一个状态;} else if (当前为运行状态) {运行状态的处理;下一个状态;} ...
    这样的方式来进行状态的处理与转换,这也是状态机的一种实现方式。

    所以呢?状态机是什么?我的理解就是:状态机就是控制物体状态转换的逻辑,也就是上面这一堆
    if () {} else if () {} ...

    为什么搜到的多数博客中都把状态机描述得很复杂呢?因为状态机本身就是一套状态转换逻辑,只不过它是为了处理更复杂的物体的状态转换而编写的。

    所以状态机的运用,在于怎样合理、高效的实现状态的转换,把原本需要使用大量 if .. else if ..来处理的转换逻辑描述的更清晰。


    好了,昨天写的转换看起来还比较清晰(一点也不高端,就是把if...else if...换成了swith...case...),但是如果需要扩展新状态,就要改动原来的swich...case...了。

    所以今天换一种实现方式:为每个状态实现一个状态类。


    详细设计:

    • 状态类的基类
      * 一个[执行逻辑处理]函数
      * 一个[电梯]对象
      * 一个[设置电梯]函数
    • 关门[状态类]
      * ...
    • 开门[状态类]
      * ...
    • 运动[状态类]
      * ...
    • 停止[状态类]
      * ...
      *电梯[类]
      * 关门状态类[指针]
      * 开门状态类[指针]
      * 运动状态类[指针]
      * 停止状态类[指针]
      * 当前状态类[指针]
      * 设置状态[函数]
      * 按下电梯[函数]

    源文件:

    //
    //  ElevatorTwo.hpp
    //  QFLTest
    //
    //  Created by QuFangliu on 16/9/19.
    //
    //
    
    #ifndef ElevatorTwo_hpp
    #define ElevatorTwo_hpp
    
    #include <stdio.h>
    
    //预先声明一下,在state中需要用到
    class ElevatorTwo;
    
    //Elevator has four status : closing, opening, running, stopping
    //Four status correspond four classes. All of them inherited from one base state class.
    class ElevatorStateBase
    {
    public:
        virtual ~ElevatorStateBase() {}
        
        //某个状态需要执行的逻辑
        virtual void excute() = 0;
        
        //设置电梯类
        void setElevator(ElevatorTwo *pElevator) { m_pElevator = pElevator; }
    protected:
        //保存一个ElevatorTow的指针,为了修改它的状态
        ElevatorTwo *m_pElevator;
    };
    
    //四种状态的类
    //Closing
    class ElevatorStateClosing : public ElevatorStateBase
    {
    public:
        ElevatorStateClosing();
        virtual ~ElevatorStateClosing();
        
        //执行逻辑
        virtual void excute() override;
    };
    
    //Opening
    class ElevatorStateOpening : public ElevatorStateBase
    {
    public:
        ElevatorStateOpening();
        virtual ~ElevatorStateOpening();
        
        //执行逻辑
        virtual void excute() override;
    };
    
    //Running
    class ElevatorStateRunning : public ElevatorStateBase
    {
    public:
        ElevatorStateRunning();
        virtual ~ElevatorStateRunning();
        
        //执行逻辑
        virtual void excute() override;
    };
    
    //Stopping
    class ElevatorStateStopping : public ElevatorStateBase
    {
    public:
        ElevatorStateStopping();
        virtual ~ElevatorStateStopping();
        
        //执行逻辑
        virtual void excute() override;
    };
    
    //电梯类
    class ElevatorTwo
    {
    public:
        ElevatorTwo();
        virtual ~ElevatorTwo();
        
        //对外开放的操作
        void gotoFloor();
        
    public:
        //每个状态都有一个class
        ElevatorStateBase *m_pStateClosing = nullptr;
        ElevatorStateBase *m_pStateOpening = nullptr;
        ElevatorStateBase *m_pStateRunning = nullptr;
        ElevatorStateBase *m_pStateStopping = nullptr;
    
        //设置状态
        void setState(ElevatorStateBase *pState) { m_pState = pState; pState->excute();}
        
    private:
        //当前状态
        ElevatorStateBase *m_pState = m_pStateClosing;  //默认关闭状态
    };
    
    #endif /* ElevatorTwo_hpp */
    
    //
    //  ElevatorTwo.cpp
    //  QFLTest
    //
    //  Created by QuFangliu on 16/9/19.
    //
    //
    
    #include "ElevatorTwo.hpp"
    #include <iostream>
    
    #define QFLLOG(_text_)  do  \
                            {   \
                                std::cout << _text_ << std::endl;   \
                            } while (false) \
    
    //ElevatorStateBase不需要在这里写实现部分了,它是一个抽象类,不能实例化
    
    //Closing
    ElevatorStateClosing::ElevatorStateClosing()
    {
        m_pElevator = nullptr;
    }
    ElevatorStateClosing::~ElevatorStateClosing()
    {
        
    }
    void ElevatorStateClosing::excute()
    {
        //Closing的逻辑部分
        
        //关门之后,进行一些处理
        QFLLOG("Closing->关上电梯门,等待有人按电梯");
        
        //处理完成,根据条件判断下一状态
        if (rand() % 100 > 50) {
            //假设50%几率,电梯里面没有人(这里的关闭可能是之前一轮运行完后的关闭)
            QFLLOG("Closing->没有要去的楼层,停在这个状态");
        }
        else {
            //50%几率,有人选择了下一个楼层
            QFLLOG("Closing->准备前往下一个目标楼层");
            m_pElevator->setState(m_pElevator->m_pStateRunning);
        }
    }
    
    //Opening
    ElevatorStateOpening::ElevatorStateOpening()
    {
        m_pElevator = nullptr;
    }
    ElevatorStateOpening::~ElevatorStateOpening()
    {
        
    }
    void ElevatorStateOpening::excute()
    {
        //Opening的逻辑部分
        
        //开门之后,进行一些处理
        QFLLOG("Opening->打开电梯门");
        
        //处理完成,下一状态就是关门(转换到下一状态这件事,可以由某些条件触发)
        QFLLOG("Opening->上客、下客完成,准备关门");
        m_pElevator->setState(m_pElevator->m_pStateClosing);
    }
    
    //Running
    ElevatorStateRunning::ElevatorStateRunning()
    {
        m_pElevator = nullptr;
    }
    ElevatorStateRunning::~ElevatorStateRunning()
    {
        
    }
    void ElevatorStateRunning::excute()
    {
        //Running的逻辑部分
        
        //运行到指定的楼层
        QFLLOG("Running->运行前往指定的楼层(加速,匀速,减速)");
        
        //处理完成,下一状态就是保持电梯停止(状态转换事件可以由条件触发)
        QFLLOG("Running->到了指定楼层,准备停止");
        m_pElevator->setState(m_pElevator->m_pStateStopping);
    }
    
    //Stopping
    ElevatorStateStopping::ElevatorStateStopping()
    {
        m_pElevator = nullptr;
    }
    ElevatorStateStopping::~ElevatorStateStopping()
    {
        
    }
    void ElevatorStateStopping::excute()
    {
        //Stopping的逻辑部分
        
        //保持电梯停止
        QFLLOG("Stopping->保持电梯停止(刹车,锁定)");
        
        //处理完成,下一状态就是开门(状态转换可以根据轿箱内是否有人来进行条件触发)
        QFLLOG("Stopping->电梯锁定到楼层,准备开门");
        m_pElevator->setState(m_pElevator->m_pStateOpening);
    }
    
    //电梯类
    ElevatorTwo::ElevatorTwo()
    {
        //初始化自己的几个状态
        m_pStateClosing = new ElevatorStateClosing();
        m_pStateOpening = new ElevatorStateOpening();
        m_pStateRunning = new ElevatorStateRunning();
        m_pStateStopping = new ElevatorStateStopping();
        
        //状态类设置"控制"类
        m_pStateClosing->setElevator(this);
        m_pStateOpening->setElevator(this);
        m_pStateRunning->setElevator(this);
        m_pStateStopping->setElevator(this);
    }
    
    ElevatorTwo::~ElevatorTwo()
    {
        //注意,要清内存噢
        delete m_pStateClosing;
        delete m_pStateOpening;
        delete m_pStateRunning;
        delete m_pStateStopping;
    }
    
    void ElevatorTwo::gotoFloor()
    {
        //用户的操作,必定是上或者下,也就是从这个事件开始改变状态
        
        //事件导致的状态就是开门
        this->setState(m_pStateOpening);
    }
    

    测试代码:

        //初始化电梯
        auto pElevator = new ElevatorTwo();
        //按电梯咯
        pElevator->gotoFloor();
    

    输出结果(因为用到了随机数,所以输出结果类似,但不唯一):
    Opening->打开电梯门
    Opening->上客、下客完成,准备关门
    Closing->关上电梯门,等待有人按电梯
    Closing->准备前往下一个目标楼层
    Running->运行前往指定的楼层(加速,匀速,减速)
    Running->到了指定楼层,准备停止
    Stopping->保持电梯停止(刹车,锁定)
    Stopping->电梯锁定到楼层,准备开门
    Opening->打开电梯门
    Opening->上客、下客完成,准备关门
    Closing->关上电梯门,等待有人按电梯
    Closing->没有要去的楼层,停在这个状态


    示例总结:
    1.[电梯]类,其实不需要持有各个状态的指针,当需要转换到某个状态时,生成一个新的状态对象即可。
    2.需要扩展状态时,例如[运行]状态的逻辑处理中可能需要新增一个[故障]状态,此时只需要新增一个[故障]状态类,然后在[运行]状态逻辑中,满足故障条件时转换到[故障]状态即可。


    总结:
    1.状态机,就是一套用于控制物体状态转换的逻辑。实现状态机控制的首要条件就是清楚的划分物体的状态。
    2.状态机有各种各样的实现方式,实现时需要考虑到子新增状态。

    相关文章

      网友评论

        本文标题:[因为我不懂啊]-什么是状态机编程(设计模式)(2)

        本文链接:https://www.haomeiwen.com/subject/ulwgettx.html