状态模式
如何去描述状态机?
假设你需要实例化一台电梯,并模仿出电梯的四个状态:开启、关闭、运行、停止。也许你会这么写
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();
}
状态模式的优势:
当由新的状态加入时,只需要扩展子类,而不需要过多地更改原有代码。遵守了开闭原则。
当动作和状态更新等逻辑交由状态类内部实现,实现了单一性设计原则。
参考
Java设计模式——状态模式(STATE PATTERN)_java中state pattern-CSDN博客