(十一)Head first design patterns状态模式(c++)

参考:Java设计模式——状态模式(STATE PATTERN)_java中state pattern-CSDN博客

状态模式

如何去描述状态机?

假设你需要实例化一台电梯,并模仿出电梯的四个状态:开启、关闭、运行、停止。也许你会这么写

class ILift{
public:
    virtual void open(){}
    virtual void close(){}
    virtual void run(){}
    virtual void stop(){}
};
class Lift : public ILift{
public:
    void open(){ std::cout << "电梯门关闭..." << std::endl; }
    void close(){ std::cout << "电梯门开启..." << std::endl; }
    void run(){ std::cout << "电梯上下跑起来..." << std::endl; }
    void stop(){ std::cout << "电梯停止了..." << std::endl; }
};
int main(){
    ILift* lift = new Lift();
    lift->open();
    lift->close();
    lift->run();
    lift->stop();
}

这样写未免太草率了。因为电梯在门开启的时候一般是不能运行的,在运行的时候一般也不会门开启,而在停止工作状态一般不会再去执行关门这个动作。所以需要设置一些状态去限制这台电梯的行为。于是在类Lift中存储电梯目前的状态,在执行每个动作的时候用swith分支来判断当前动作是否有效,以及更新当前状态。于是有了下面的代码

class ILift{
public:
    virtual void setState(int state){};
    virtual void open(){}
    virtual void close(){}
    virtual void run(){}
    virtual void stop(){}
};
class Lift : public ILift{
public:
    Lift(int state):state(state){}
    void setState(int state){ this->state = state; }
    void close(){
        switch(state){
            case OPENING_STATE:
                closeWithoutLogic();
                setState(CLOSING_STATE);
                break;
            case CLOSING_STATE:
                break;
            case RUNNING_STATE:
                break;
            case STOPPING_STATE:
                break;
        }
    }
    void open(){
        switch(state){
            case OPENING_STATE:
                break;
            case CLOSING_STATE:
                openWithoutLogic();
                setState(OPENING_STATE);
                break;
            case RUNNING_STATE:
                break;
            case STOPPING_STATE:
                openWithoutLogic();
                setState(OPENING_STATE);
        }
    }
    void run(){
        switch(state){
            case OPENING_STATE:
                break;
            case CLOSING_STATE:
                runWithoutLogic();
                setState(RUNNING_STATE);
                break;
            case RUNNING_STATE:
                break;
            case STOPPING_STATE:
                runWithoutLogic();
                setState(RUNNING_STATE);
        }
    }
    void stop(){
        switch(state){
            case OPENING_STATE:
                break;
            case CLOSING_STATE:
                stopWithoutLogic();
                setState(STOPPING_STATE);
                break;
            case RUNNING_STATE:
                stopWithoutLogic();
                setState(STOPPING_STATE);
                break;
            case STOPPING_STATE:
                break;
        }
    }
    void closeWithoutLogic(){ std::cout << "电梯门关闭..." << std::endl; }
    void openWithoutLogic() { std::cout << "电梯门开启..." << std::endl; }
    void runWithoutLogic()  { std::cout << "电梯上下跑起来..." << std::endl; }
    void stopWithoutLogic() { std::cout << "电梯停止了..." << std::endl; }
private:
    int state;
};
int main(){
    ILift* lift = new Lift(STATE(OPENING_STATE));
    lift->close(); // 关闭
    lift->open();  // 开启
    lift->run();   // 无动作
    lift->stop();  // 无动作
    lift->close(); // 关闭
}

这个类的实现代码特别长,内部包含了太多的switch语句。而且当需要增加状态时,比如说电梯停电状态和电梯维修状态,就需要去更改里面的switch语句。这样写违背了开闭原则以及单一性原则。为了在增加状态的时候尽量少的修改原有代码,可以将swtich中的每个状态抽离出来单独包装成类。

创建context类,在context类中存有LiftState对象用来记录当前的状态。此时context目前拥有四种状态对象,用指针维护。每种状态的逻辑由各自类在内部实现。当电梯发生动作,即context调用函数时,函数内部会调用当前state对应的方法,于是这部分逻辑转交由state内部实现。具体代码如下:

#include<iostream>
#include<string>

using namespace std;

class ContextBase;
class LiftState{
public:
    void setContext(ContextBase* context){ this->mContext = context; }
    virtual void open(){}
    virtual void close(){}
    virtual void run(){}
    virtual void stop(){}
    ContextBase* getContext(){ return mContext; }
public:
    ContextBase* mContext;
};
class ContextBase{
public:
    ContextBase(){}
    virtual LiftState* getLiftState(){}
    virtual LiftState* getOpenningState(){}
    virtual LiftState* getClosingState(){}
    virtual LiftState* getRunningState(){}
    virtual LiftState* getStoppingState(){}
    virtual void setLiftState(LiftState* liftState){}
    virtual void open(){}
    virtual void close(){}
    virtual void run(){}
    virtual void stop(){}
public:
    LiftState* liftState;
};
class OpenningState : public LiftState{
    void open(){
        std::cout << "lift open..." << std::endl;
    }
    void close(){
        mContext->setLiftState(mContext->getClosingState());
        mContext->getLiftState()->close();
    }
    void run(){}
    void stop(){}
};
class ClosingState : public LiftState{
    void open(){
        mContext->setLiftState(mContext->getOpenningState());
        mContext->getLiftState()->open();
    }
    void close(){
        std::cout << "lift close..." << std::endl;
    }
    void run(){
        mContext->setLiftState(mContext->getRunningState());
        mContext->getLiftState()->run();
    }
    void stop(){
        mContext->setLiftState(mContext->getStoppingState());
        mContext->getLiftState()->stop();
    }
};
class RunningState : public LiftState{
    void open(){
    }
    void close(){
    }
    void run(){
        std::cout << "lift running..." << std::endl;
    }
    void stop(){
        mContext->setLiftState(mContext->getStoppingState());
        mContext->getLiftState()->stop();
    }
};
class StoppingState : public LiftState{
    void open(){
        mContext->setLiftState(mContext->getOpenningState());
        mContext->getLiftState()->open();
    }
    void close(){
    }
    void run(){
        mContext->setLiftState(mContext->getRunningState());
        mContext->getLiftState()->run();
    }
    void stop(){
        std::cout << "lift stopping..." << std::endl;
    }
};

class Context : public ContextBase{
public:
    Context(){}
    LiftState* getLiftState(){
        return liftState;
    }
    LiftState* getOpenningState(){
        return openningState;
    }
    LiftState* getClosingState(){
        return closingState;
    }
    LiftState* getRunningState(){
        return runningState;
    }
    LiftState* getStoppingState(){
        return stoppingState;
    }
    void setLiftState(LiftState* liftState){
        this->liftState = liftState;
        this->liftState->setContext(this);
    }
    void open(){ liftState->open(); }
    void close(){ liftState->close(); }
    void run(){ liftState->run(); }
    void stop(){ liftState->stop(); }
public:
    LiftState* openningState = new OpenningState();
    LiftState* closingState  = new ClosingState();
    LiftState* runningState  = new RunningState();
    LiftState* stoppingState = new StoppingState();
};

int main(){
    Context* context = new Context;
    context->setLiftState(new ClosingState());
    context->open();
    context->close();
    context->run();
    context->stop();
}

状态模式的优势:

当由新的状态加入时,只需要扩展子类,而不需要过多地更改原有代码。遵守了开闭原则。

当动作和状态更新等逻辑交由状态类内部实现,实现了单一性设计原则。