状态模式:事物在不同状态下会有不同表现(动作),经过不同动作后,又会转移到下一个不同的状态。这种问题可以用Switch/Case(if-else)搞定, 但如果case语句很多,就很麻烦,这种实现也没有将逻辑和动作分离,后期维护得不到保证!加case语句要修改原来代码,违反了“对扩展开放,对修改关闭”的原则!State 模式将每一个case分支都封装到独立的类中!
状态模式的具体实现类中有一个指向Context的引用。State 模式很好地实现了对象的状态逻辑和动作实现的分离, 状态逻辑分布在State 的派生类中实现,而动作实现则可以放在 Context 类中实现(这也是为什么 State 派生类需要拥有一个指向 Context 的指针)。这使得两者的变化相互独立, 改变 State 的状态逻辑可以很容易复用 Context 的动作, 也可以在不影响 State 派生类的前提下创建Context 的子类来更改或替换动作实现。
上下文环境(Context):定义客户需要的接口并维护一个ConcreteState实例,将与状态相关的操作委托给它来处理。
抽象状态(State):定义一个接口以封装使用上下文环境的的一个特定状态相关的行为。
具体状态(Concrete State):实现抽象状态定义的接口, 每一子类实现一个与Context的一个状态相关的行为。
这个状态模式,网上的实现我觉得都不怎么好,主要是实现细节方面的!这一篇,每次状态转换都new一个对象,delete一个对象,申请&释放内存好像挺耗时的!它描述的状态不是封闭的,我自己在实现的时候,就出现了问题--说出来可能丢人,就是new一个对象传入时,如果对象定义在后面C++是识别不到的,前向声明也没用,只能把.h和.cpp文件分开写,然后互相包含对方的.h文件!
另一篇,我觉得实现的很好,不过是PHP版本的,PHP是弱类型语言,有一些方便之处,比如所有状态类事先在Context类构造函数中new好。我在用C++改写时遇到了问题,什么头文件你包含我,我包含你的,太烦了,没成功!就简化成下面这样吧,不停申请&释放是挺耗时的。可以理解为一个开关,只有开和关两种状态!
class Context;
class LiftState {
public:
LiftState();
virtual ~LiftState();
virtual void open(Context*) = 0;
virtual void close(Context*) = 0;
};
class Context
{
public:
Context();
~Context();
void setLiftState(LiftState* ls);
LiftState* getLiftState();
void open();
void close();
private:
LiftState* m_liftState;
};
LiftState::LiftState() {}
LiftState::~LiftState() {}
Context::Context() { m_liftState = NULL; }
Context::~Context() {}
void Context::setLiftState(LiftState* ls) {
if (m_liftState) delete m_liftState;
m_liftState = ls;
}
LiftState* Context::getLiftState() { return m_liftState; }
void Context::open() { m_liftState->open(this); }
void Context::close() { m_liftState->close(this); }
class OpenState : public LiftState {
public:
OpenState();
void open(Context* );
void close(Context* );
};
OpenState::OpenState() {}
void OpenState::open(Context* ctx) {}
void OpenState::close(Context* ctx) {
cout << "open->close!" << endl;
ctx->setLiftState(new CloseState());
ctx->getLiftState()->close(ctx);
}
#include"openstate.h"
class CloseState : public LiftState {
public:
CloseState();
void open(Context* );
void close(Context* );
};
#include"openstate.h"
#include"closestate.h"
CloseState::CloseState() {}
void CloseState::open(Context* ctx) {
cout << "close->open!" << endl;
ctx->setLiftState(new OpenState());
ctx->getLiftState()->open(ctx);
}
void CloseState::close(Context* ctx) {}
用户代码:
int main()
{
Context* ct = new Context();
ct->setLiftState(new OpenState());
ct->open();
ct->close();
ct->open();
return 0;
}
如果有想测试这个代码的,记得把各个代码块分文件存放!因为两个状态的CPP文件相互包含了对方的头文件,相互包含只能是声明,不能是定义!
代码里,state类里是通过传参的方式使用Context类的,而不能组合一个Context*类型的变量,因为Context类里已经组合了一个State*的变量了,相互包含的话,上面那条delete语句后,把调用setLiftState函数的指针对象自身都删除了,因为删除的State类对象指针中包含了Context对象指针!