定义:
当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类。(状态的变更引起了行为的变更,从外部看起来好像这个对象对应的类发生了改变一样)
示例一:状态模式(通用版)
1. 类图 26-5
2. 类图说明
State 抽象状态角色
接口或抽象类,负责对象状态定义,并且封装环境角色以实现状态切换。
ConcreteState 具体状态角色
每一个具体状态必须完成两个职责:本状态的行为管理以及趋向状态处理,即本状态要做的事情,以及本状态如何过度到其他状态。
Context 环境角色
定义客户端需要的接口,并且负责具体状态的切换。
3. 代码清单
#include <QCoreApplication>
#include <QDebug>
class Context;
//抽象环境角色
class State
{
public:
void setContext(Context &context)
{
this->m_context = &context;
}
virtual void handle1() = 0;
virtual void handle2() = 0;
protected:
Context *m_context;
};
//具体环境角色
class Context
{
public:
State* getCurrentState()
{
return this->m_curState;
}
void setCurrentState(State* state)
{
this->m_curState = state;
this->m_curState->setContext(*this);
}
void handle1()
{
this->m_curState->handle1();
}
void handle2()
{
this->m_curState->handle2();
}
public:
static State* m_state1;
static State* m_state2;
private:
State* m_curState;
};
//环境角色
class State1:public State
{
public:
virtual void handle1()
{
qDebug() << "this is handle1";
}
virtual void handle2()
{
this->m_context->setCurrentState(this->m_context->m_state2);
this->m_context->handle2();
}
};
class State2:public State
{
public:
virtual void handle1()
{
this->m_context->setCurrentState(this->m_context->m_state1);
this->m_context->handle1();
}
virtual void handle2()
{
qDebug() << "this is handle2";
}
};
State* Context::m_state1 = new State1();
State* Context::m_state2 = new State2();
int main()
{
Context context;
State* state1 = new State1();
context.setCurrentState(state1);
context.handle1();
context.handle2();
delete state1;
return 0;
}
示例二:电梯
1. 类图 26-1
2. 分析特定状态
- 敞门状态。这时可以关门
- 闭门状态。这时可以开门,停止,运行
- 运行状态。这时只能停止
- 停止状态。这时可以运行和开门
3. 电梯状态和动作之间关系图 26-2
4. 扩展状态后的类图 26-3
5. 扩展状态后代码
//电梯接口
class ILift
{
public:
virtual void open() = 0;
virtual void close() = 0;
virtual void run() = 0;
virtual void stop() = 0;
virtual void setState(int state) = 0;
public:
static const int OPENING_STATE = 1;
static const int CLOSING_STATE = 2;
static const int RUNNING_STATE = 3;
static const int STOPPING_STATE = 4;
};
//电梯实现
class Lift:public ILift
{
public:
virtual void open()
{
switch(this->m_state)
{
case OPENING_STATE:
break;
case CLOSING_STATE:
this->openWithoutLogic();
this->setState(OPENING_STATE);
break;
case RUNNING_STATE:
break;
case STOPPING_STATE:
this->openWithoutLogic();
this->setState(OPENING_STATE);
break;
}
}
virtual void close()
{
switch(this->m_state)
{
case OPENING_STATE:
this->closeWithoutLogic();
this->setState(CLOSING_STATE);
break;
case CLOSING_STATE:
break;
case RUNNING_STATE:
break;
case STOPPING_STATE:
break;
}
}
virtual void run()
{
switch(this->m_state)
{
case OPENING_STATE:
break;
case CLOSING_STATE:
this->runWithoutLogic();
this->setState(RUNNING_STATE);
break;
case RUNNING_STATE:
break;
case STOPPING_STATE:
this->runWithoutLogic();
this->setState(RUNNING_STATE);
break;
}
}
virtual void stop()
{
switch(this->m_state)
{
case OPENING_STATE:
break;
case CLOSING_STATE:
this->stopWithoutLogic();
this->setState(CLOSING_STATE);
break;
case RUNNING_STATE:
this->stopWithoutLogic();
this->setState(CLOSING_STATE);
break;
case STOPPING_STATE:
break;
}
}
virtual void setState(int state)
{
this->m_state = state;
}
private:
void closeWithoutLogic()
{
qDebug() << "close";
}
void openWithoutLogic()
{
qDebug() << "open";
}
void runWithoutLogic()
{
qDebug() << "run";
}
void stopWithoutLogic()
{
qDebug() << "stop";
}
private:
int m_state;
};
int main()
{
ILift *lift = new Lift();
lift->setState(ILift::STOPPING_STATE);
lift->open();
lift->close();
lift->run();
lift->stop();
delete lift;
return 0;
}
6. 存在的问题
电梯实现类Lift有点长。里面使用了大量的switch...case,在业务复杂的情况下,程序会更长。
扩展性非常差。电梯还有通电和断点两个状态,若要增加这两个方法,原有4个方法都需要增加判断条件
非常规状态无法实现。
7. 使用状态模式,类图 26-4
8. 使用状态模式,代码
/ ******************************************* 声明 ********************************* ///
class LiftState;
//上下文类 声明
class Context
{
public:
Context();
LiftState* getLiftState();
void setLiftState(LiftState *liftState);
void open();
void close();
void run();
void stop();
public:
static LiftState *m_openningState;
static LiftState *m_closingState;
static LiftState *m_runningState;
static LiftState *m_stoppingState;
private:
LiftState *m_liftState;
};
//抽象电梯状态 声明
class LiftState
{
public:
void setContext(Context &context);
virtual void open() = 0;
virtual void close() = 0;
virtual void run() = 0;
virtual void stop() = 0;
protected:
Context *m_context;
};
//关闭状态 声明
class ClosingState:public LiftState
{
public:
virtual void open();
virtual void close();
virtual void run();
virtual void stop();
};
//敞门状态 声明
class OpenningState:public LiftState
{
public:
virtual void open();
virtual void close();
virtual void run();
virtual void stop();
};
//运行状态 声明
class RunningState:public LiftState
{
public:
virtual void open();
virtual void close();
virtual void run();
virtual void stop();
};
//停止状态 声明
class StoppingState:public LiftState
{
public:
virtual void open();
virtual void close();
virtual void run();
virtual void stop();
};
/// *********************************************** 定义 ************************************************* ///
//上下文类 定义
LiftState* Context::m_openningState = new OpenningState();
LiftState* Context::m_closingState = new ClosingState();
LiftState* Context::m_runningState = new RunningState();
LiftState* Context::m_stoppingState = new StoppingState();
Context::Context()
{
this->setLiftState(this->m_closingState);
}
LiftState* Context::getLiftState()
{
return this->m_liftState;
}
void Context::setLiftState(LiftState* liftState)
{
this->m_liftState = liftState;
this->m_liftState->setContext(*this);
}
void Context::open()
{
this->m_liftState->open();
}
void Context::close()
{
this->m_liftState->close();
}
void Context::run()
{
this->m_liftState->run();
}
void Context::stop()
{
this->m_liftState->stop();
}
//抽象电梯状态 定义
void LiftState::setContext(Context &context)
{
this->m_context = &context;
}
//关闭状态 定义
void ClosingState::open()
{
this->m_context->setLiftState(m_context->m_openningState);
this->m_context->getLiftState()->open();
}
void ClosingState::close()
{
qDebug() << "close";
}
void ClosingState::run()
{
this->m_context->setLiftState(m_context->m_runningState);
this->m_context->getLiftState()->run();
}
void ClosingState::stop()
{
this->m_context->setLiftState(m_context->m_stoppingState);
this->m_context->getLiftState()->stop();
}
//敞门状态 定义
void OpenningState::open()
{
qDebug() << "open";
}
void OpenningState::close()
{
this->m_context->setLiftState(m_context->m_closingState);
this->m_context->getLiftState()->close();
}
void OpenningState::run(){}
void OpenningState::stop(){}
//运行状态 定义
void RunningState::open(){}
void RunningState::close(){}
void RunningState::run()
{
qDebug() << "run";
}
void RunningState::stop()
{
this->m_context->setLiftState(m_context->m_stoppingState);
this->m_context->getLiftState()->stop();
}
//停止状态 定义
void StoppingState::open()
{
this->m_context->setLiftState(m_context->m_openningState);
this->m_context->getLiftState()->open();
}
void StoppingState::close(){}
void StoppingState::run()
{
this->m_context->setLiftState(m_context->m_runningState);
this->m_context->getLiftState()->run();
}
void StoppingState::stop()
{
qDebug() << "stop";
}
int main()
{
Context context;
context.open();
context.close();
context.run();
context.stop();
return 0;
}
三、状态模式的应用
1. 优点:
- 结构清晰。避免了过多的条件嵌套导致的程序的复杂性,提高可维护性。
- 遵循设计原则。很好地体现了开闭原则和单一职责原则,每个状态都是一个子类,要增加状态就要增加子类,需要修改状态,只修改一个子类就好
- 封装性好。状态变换放置到类的内部实现,外部的调用不用知道类内部如何实现状态和行为的变换。
2. 缺点:
子类会太多,也就是类膨胀。如果完全使用状态模式就会有太多的子类,不好管理,需要在项目中自己衡量。有很多方式可以解决这个状态问题,如在数据库中建立一个状态表,然后根据状态执行响应的操作。
3. 使用场景:
- 行为随状态改变而改变的场景。例:权限设计,人员的状态不同即使执行相同的行为结果也会不同,在这种情况下需要考虑使用状态模式。
- 条件、分支判断语句的替代者。在程序中大量使用switch或if潘丹语句会导致程序结构不清晰,逻辑混乱。
4. 注意事项:
状态模式适用于当前某个对象在它的状态发生改变时,它的行为也随着发生比较大的变化,也就是说在行为受状态约束的情况下可以使用状态模式,而且使用时对象的状态最好不要超过5个。
参考文献《秦小波. 设计模式之禅》(第2版) (华章原创精品) 机械工业出版社