一、状态模式
状态模式,允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。它的实现方式和策略模式相似,目的都是对if...else语句进行优化,只不过,策略模式通过外部传入枚举、条件来决定选择哪一种操作方式。策略模式中的枚举、条件相当于状态模式中的状态。状态不需要由外部传入,而是随着自身的操作来自动地变化。
例如类Context 包含以下的流程图,context 有两个方法run 和fallback,四种状态,例如当状态Ready执行一次run操作状态变成了d式,允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。它的实现方式和策略模式相似,目的都是对if...else语句进行优化,只不过,策略模式通过外部传入枚举、条件来决定选择哪一种操作方式。策略模式中的枚举、条件相当于状态模式中的状态。状态不需要由外部传入,而是随着自身的操作来自动地变化。
例如类Context 包含以下的流程图,context 有两个方法run 和fallback,四种状态,例如当状态Ready执行一次run操作状态变成了executing,当再执行run时就变成了完成状态,如果执行fallback就变成了hangup状态。
本例目标是为了展示 2*100 的计算和输出过程。
方法一
enum State{
Ready,
Executing,
Finish,
Hangup
}
class Context{
State state;
int result;
public:
void run(){
if(state == Ready){
result = 2;
state = Executing;
}else if(state == Executing){
result *=100;
state =Finish;
}else if(state == Finish){
cout<<"result:"<<result;
result=0;
state =Ready;
}else if(state ==Hangup){
state = Executing;
}
}
void fallback(){
if(state == Ready){
state = Ready;
result=0;
}else if(state == Executing){
state =Hangup;
}else if(state == Finish){
result /=100;
state =Executing;
}else if(state ==Hangup){
state = Ready;
result =2;
}
}
}
当操作方法发生需求变更时,方法一代码这样写影响范围就会覆盖整个流程,代码不易维护,不易扩展。如果增加状态,那么枚举要新增,run方法和fallback方法也要跟者新增,代码会变得越来越臃肿。
方法二:利用状态模式,把枚举变成类对象
enum State{
Ready,
Executing,
Finish,
Hangup
}
/********状态类,可用单例模式进行优化***********/
class IState{
virtual State oprationRun(Context *)=0;
virtual State oprationFallback(Context *)=0;
};
class ReadyState:public IState{
public:
virtual State oprationRun(Context * context){
context->result=2;
return Executing;
}
virtual State oprationFallback(Context * context){
context->result=0;
return Ready;
}
};
class ExecutingState:public IState{
public:
virtual State oprationRun(Context * context){
context->result *=100;
return Finish;
}
virtual State oprationFallback(Context * context){
return Hangup;
}
};
class FinishState:public IState{
public:
virtual State oprationRun(Context * context){
cout<<"result:"<< context->result;
context->result =0;
return Ready;
}
virtual State oprationFallback(Context * context){
context->result /=100;
return Executing;
}
};
class HangupState:public IState{
public:
virtual State oprationRun(Context * context){
return Executing;
}
virtual State oprationFallback(Context * context){
context->result=2;
return Ready;
}
};
/*********Context自动地完成操作******************/
class Context{
State state; //状态属性,这个属性变化,操作也会动态地变化
IState * iState; //状态对象
int result;
private:
void initIState(){
switch(state){
case Ready: iState =new ReadyState(); break;
case Executing:iState =new ExecutingState(); break;
case Finish: iState =new FinishState(); break;
case Hangup: iState =new HangupState(); break;
Default:
....ERROR....
}
}
public:
Context(){state =Ready;result =0;}
void run(){
initIState();
state =iState->oprationRun(this);
}
void fallback(){
initIState();
state =iState->oprationFallback(this);
}
}
这样的好处就是当变更状态内部操作时,不需要改动Context方法。当新增一个状态时,只需要新增一个状态类和一个映射就可以,不需要变动其他状态方法。这样编码结构清晰,误操作的可能变小,变得更容易维护。
最后调用过程
void main(){
Context context; //result =0 ,状态为Ready
context.run(); //result =2 ,状态从Ready变成Executing;
context.run(); //result =200 ,状态从Executing变成finish;
context.run(); //输出 200 ,状态从finish变成Ready; result =0
}
二、备忘录模式
备忘录模式,在不破坏封装性的前提下,捕获对象内部的状态,并将者着状态放在外部进行保存(比如文件,数据库)。方便以后对象恢复到原先的状态。相当于游戏的存档。
在模式中,具体实现的方式就是新增一个类,用来存储原先对象重要的属性。
继续按照上述例子,新增备忘录类和get/set备忘录方法
enum State{
Ready,
Executing,
Finish,
Hangup
}
/********状态类,可用单例模式进行优化***********/
class IState{
virtual State oprationRun(Context *)=0;
virtual State oprationFallback(Context *)=0;
};
class ReadyState:public IState{
public:
virtual State oprationRun(Context * context){
context->result=2;
return Executing;
}
virtual State oprationFallback(Context * context){
context->result=0;
return Ready;
}
};
class ExecutingState:public IState{
public:
virtual State oprationRun(Context * context){
context->result *=100;
return Finish;
}
virtual State oprationFallback(Context * context){
return Hangup;
}
};
class FinishState:public IState{
public:
virtual State oprationRun(Context * context){
cout<<"result:"<< context->result;
context->result =0;
return Ready;
}
virtual State oprationFallback(Context * context){
context->result /=100;
return Executing;
}
};
class HangupState:public IState{
public:
virtual State oprationRun(Context * context){
return Executing;
}
virtual State oprationFallback(Context * context){
context->result=2;
return Ready;
}
};
/*********Context自动地完成操作******************/
class Context{
State state; //状态属性,这个属性变化,操作也会动态地变化
IState * iState; //状态对象
int result;
private:
void initIState(){
switch(state){
case Ready: iState =new ReadyState(); break;
case Executing:iState =new ExecutingState(); break;
case Finish: iState =new FinishState(); break;
case Hangup: iState =new HangupState(); break;
Default:
....ERROR....
}
}
public:
Context(){state =Ready;result =0;}
void run(){
initIState();
state =iState->oprationRun(this);
}
void fallback(){
initIState();
state =iState->oprationFallback(this);
}
/****创建存档和读取存档*****/
ContextMemento createMemento(){
return ContextMemento (state,result);
}
void setMemento(ContextMemento& cm){
state = cm.oldstate;
result = cm.oldresult;
}
}
/**********备忘录对象************/
class ContextMemento{
public:
State oldstate; //状态属性,这个属性变化,操作也会动态地变化
int oldresult;
ContextMemento (State s, int r){
oldstate= s; oldresult=r;
}
}
void main(){
Context context; //result =0 ,状态为Ready
context.run(); //result =2 ,状态从Ready变成Executing;
ContextMemento contextMemento = context.createMemento();
context.run(); //result =200 ,状态从Executing变成finish;
context.setMemento(contextMemento ); //result =2 状态为 Executing
context.run(); //result =200 ,状态从Executing变成finish;
context.run();//输出 200 ,状态从finish变成Ready; result =0
}
备忘录的目的就是为了存档,现如今,储存/读取对象有更方便的方式。备忘录模式在如今有些过时。更有效的方式可以替代备忘录模式,例如对象序列化,对象编码等。但是备忘录的思想还是没变:
1)不破坏原对象的封装性
2) 获取原对象重要的属性,并对这些属性进行隐藏