在《GoF 23种设计模式模式解析附C++实现源码》中,我已经给出了对于State模式的简化诠释并给出了一个C++实现的例子。再次审视以前的代码,发现有些地方还是有些生涩,并且感觉示例代码没有能够很好地诠释要表达的意思。最近因为项目中用到了一些State模式相关的知识,对State模式进行了一个重新的审视,并附上用C++实现的例子,供学习交流。
对于State模式,很多情况下和Strategy模式看起来极为相似。实际上它们都是为了解决具体子类实现抽象接口的实现异构问题而存在的,但是它们的侧重各不相同。而针对算法的异构问题,Template模式通过继承的方式来改变一部分算法实现(原子操作在不同具体子类中可以有不同实现),Strategy模式则通过组合的方式来改变整个算法(可动态替换),而State模式则强调的是针对不同的状态对象可以有不同的响应。因此State模式实际上强调的状态的概念,并且强调对状态转换的逻辑封装,即对象可能处于不同的状态下,而各个状态在响应了该状态的实现后可能会动态转到另一个状态,而这个转变我们不希望Context的参与(Context不必维护这个转换)。状态机在编译原理的DFA/NDFA中很常见,针对一个输入字符和已有串,DFA/NDFA可能会转换到另外一个状态。
因此对于State模式有以下几个关键点:
1) State模式会处理算法的不同,但是更加关注的是状态的改变。并且对于状态的转变逻辑一般会放在State子类中实现。而对于不同状态的处理则可以放在Context类中,State子类保存一个指向Context的引用(实际上往往传递一个指向Context的指针即可,而不必在State子类真正保存一个引用),以调用这些实现。当然放在State子类中实现也无可厚非,不过为了突出重点,使用前一种方式实现更能说明问题。当然在实际开发中,完全可以不受这个制约。
2) 在具体实现过程中,对状态的改变我们会在Context类中实现(因为Context才有State的概念),而在State子类中的状态转变逻辑实现则通过调用这个实现来达到目的。当然为了不让这个改变状态的接口暴露给普通客户程序员,我们将Context中这个接口声明为private,而在将State类声明为Context的friend类,并且将State子类中状态改变逻辑实现声明为Protected,不让普通客户程序员调用。具体请参考示例代码部分。
以下就按照上面的解释给出一个C++的源码实现,首先给出其结构图:
C++实现源码为:
//context.h #ifndef _CONTEXT_H_ #define _CONTEXT_H_ class State; /** * **/ class Context { public: Context(); Context(State* state); ~Context(); void Handle(); void OperationForStateA(); void OperationForStateB(); protected: private: friend class State; //表明在State类中可以访问Context类的private字段,重要是访问ChangeState void ChangeState(State* state); private: State* _state; }; #endif //~_CONTEXT_H_ |
//context.cpp #include "Context.h" #include "State.h" #include <iostream> using namespace std; Context::Context() { } Context::Context(State* state) { this->_state = state; } Context::~Context() { delete _state; } void Context::Handle() { _state->Handle(this); } void Context::ChangeState(State* state) { ///_state->ChangeState(this,state); this->_state = state; } void Context::OperationForStateA() { cout<<"Do operation in State A "; } void Context::OperationForStateB() { cout<<"Do operation in State B "; } |
//state.h #ifndef _STATE_H_ #define _STATE_H_ class Context; //前置声明 class State { public: State(); virtual ~State(); virtual void Handle(Context* con) = 0; protected: void ChangeState(Context* con,State* st); private: //bool ChangeState(Context* con,State* st); }; class ConcreteStateA:public State { public: ConcreteStateA(); virtual ~ConcreteStateA(); void Handle(Context* con); protected: private: }; class ConcreteStateB:public State { public: ConcreteStateB(); virtual ~ConcreteStateB(); void Handle(Context* con); protected: private: }; #endif //~_STATE_H_ |
//State.cpp #include "State.h" #include "Context.h" #include <iostream> using namespace std; State::State() { } State::~State() { } void State::ChangeState(Context* con,State* st) { con->ChangeState(st); } /// ConcreteStateA::ConcreteStateA() { } ConcreteStateA::~ConcreteStateA() { } void ConcreteStateA::Handle(Context* con) { con->OperationForStateA(); cout<<":: State change from A to B"<<endl; State::ChangeState(con,new ConcreteStateB()); } /// ConcreteStateB::ConcreteStateB() { } ConcreteStateB::~ConcreteStateB() { } void ConcreteStateB::Handle(Context* con) { con->OperationForStateB(); cout<<":: State change from B to A"<<endl; State::ChangeState(con,new ConcreteStateA()); } |
//main.cpp #include "Context.h" #include "State.h" #include <iostream> using namespace std; int main(int argc,char* argv[]) { State* st = new ConcreteStateA(); Context* con = new Context(st); con->Handle(); con->Handle(); con->Handle(); if (con != NULL) delete con; if (st != NULL) st = NULL; return 0; } |
可以看到在测试程序中,三次调用con->Handle(),因为con状态的不同,可以得到以下的输出:
Do operation in State A :: State change from A to B
Do operation in State B :: State change from B to A
Do operation in State A :: State change from A to B
而con对于状态的转变一无所知,并且针对不同的状态有了不同的处理。