问题
每个人,事物在不同的状态下会有不同的表现(动作),而一个状态又会在不同表现下转移到下一个不同的状态(State)。最简单的一个生活中的例子就是:地铁入口处,如果你放入正确的地铁票,门就会打开让你通过。在出口处也就是检票,如果正确你就OK,否则就不让你通过。
当状态数目不是很多的时候,switch/case可能可以搞定,但是当数目很多的时候(实际系统中也正是如此),维护一大组的switch/case语句将是一件异常困难并且出差的事情。
状态逻辑和动作实现没有分离。在很多的系统实现中,动作的实现代码直接写在状态的逻辑当中。这带来的后果就是系统的扩展性和维护得不到保证。
state模式就是被用来解决上面列出的两个问题,在state模式中我们将状态逻辑和动作实现进行分离。当一个操作中要维护大量的case分支语句,并且这些分支依赖对象的状态。state模式将每一个分支都封装到独立的类中。
state状态模式
允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它所属的类。
分析:state模式主要解决的是在开发章时常遇到的根据不同的状态需要进行不同的处理操作的问题,而这样的问题,大部分人事采用switch/case语句进行处理的。这样会造成一个问题:分支过多,而且如果加入一个新的状态就需要对原来的代码进行编译。
state模式采用了对这些不同状态进行封装的方式处理这类问题,当状态改变的时候进行处理然后再切换到另一种状态,也就是说白状态的切换责任交给了具体的状态去负责。
state模式和strategy模式在图示上有很多相似的地方。需要说明的是两者的思想都是一致的。只不过封装的东西不同:state模式封装的是不同的状态,而strategy模式封装的是不同的算法。
小demo
state.h
#ifndef STATE_H
#define STATE_H
class State;
class Context
{
public:
Context(State* pState);
~Context();
void Request();
void ChangeState(State *pState);
private:
State *m_pState;
};
class State
{
public:
virtual ~State(){}
virtual void Handle(Context* pContext) = 0;
};
class ConcreateStateA : public State
{
public:
void Handle(Context* pContext);
};
class ConcreateStateB : public State
{
public:
void Handle(Context* pContext);
};
#endif
state.cpp
#include "State.h"
#include <iostream>
Context::Context(State* pState) : m_pState(pState)
{
}
Context::~Context()
{
delete m_pState;
m_pState = NULL;
}
void Context::Request()
{
if (NULL != m_pState)
{
m_pState->Handle(this);
}
}
void Context::ChangeState(State *pState)
{
if (NULL != m_pState)
{
delete m_pState;
m_pState = NULL;
}
m_pState = pState;
}
void ConcreateStateA::Handle(Context* pContext)
{
std::cout << "Handle by ConcreateStateA\n";
if (NULL != pContext)
{
pContext->ChangeState(new ConcreateStateB());
}
}
void ConcreateStateB::Handle(Context* pContext)
{
std::cout << "Handle by ConcreateStateB\n";
if (NULL != pContext)
{
pContext->ChangeState(new ConcreateStateA());
}
}
main.cpp
#include "State.h"
#include <stdlib.h>
int main()
{
State *pState = new ConcreateStateA();
Context *pContext = new Context(pState);
pContext->Request();
pContext->Request();
pContext->Request();
delete pContext;
system("pause");
return 0;
}
state及其子类中的操作都将Context*传入作为参数,其主要目的是state类可以通过这个指针调用Context中的方法。这也是state模式和strategy模式最大的区别。
每次调用后状态都会改变(A-B-A),因此该动作随着Context的状态的转变而获得了不同的结果。
state模式和strategy模式的区别在于它们所关注的点不尽相同:state模式主要是要适应对象对于状态改变时的不同处理策略的实现,而strategy则主要是具体算法和实现接口的解耦,strategy模式中并没有状态的概念(虽然很多时候有可以被看作是状态的概念),并且更加不关心状态的改变。
state模式很好的实现了对象的状态逻辑和动作实现的分离,状态逻辑分布在state的派生类中实现,而动作实现则可以放在context类中实现(这也是为什么state派生类需要拥有一个指向context的指针)。这使得两者的变化相互独立,改变state的状态逻辑可以很容易复用context的动作,也可以不影响state派生类的前提下创建context的子类来更改或替换动作实现。
state模式问题主要是逻辑分散化,状态逻辑分布到了很多的state的子类中,很难看到整个的状态逻辑图,这也带来了代码的维护问题。