状态模式中,类的行为是基于它的状态改变的。在状态模式中,我们创建表示各种状态的对象和一个行为随着状态的对象改变而改变的context对象。
1. 介绍
意图:允许对象再内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。
主要解决:对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为。
何时使用:代码中包含大量的与对象状态相关的条件语句。
关键代码:通常命令模式的接口中只有一个方法,而状态模式的接口中有一个过着多个方法。而且状态模式的实现类的方法啊,一般返回值或者是改变实例变量的值。也就是说,状态模式一般和对象的状态有关。实现类的方法有不同的功能,覆盖接口中的方法。状态模式和命令模式一样,也可以用于消除if…else等条件选择语句。
应用实例:1、打篮球的时候运动员可以有正常状态、不正常装填和超常状态。2、曾侯乙编钟中,“钟是抽象接口”钟A等是具体状态,“曾侯乙编钟”是具体环境(context)。
优点:1、封装了装换规则。2、枚举可能的状态,在枚举状态之前需要确定状态种类。3、将所有与某个状态有关的行为放到一个列中,并且以方便的增加新的状态,只需要改变对象状态即可改变对象的行为。4、允许状态转换逻辑与状态对象合成一体,而不是某个巨大的条件语句块。5、可以让多个黄金对象共享一个状态对象,从而减少系统中的对象的个数。
缺点:1、装填模式的使用必然会增加系统类和对象的个数。2、状态模式的结构与实现相较为复杂,如果使用了不当将导致程序结构和代码的混乱。3、状态模式对“开闭原则”的支持并不太好,对于可以切换状态的状态模式,增加了新的状态类需要修改哪些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需要修改对应类的源代码。
使用场景:1、行为随状态改变而改变的场景。2、条件、分支语句的代替者。
注意事项:在行为受状态约束的时候使用状态模式,而且状态不超过5个。
2. 实例
2.1. C++实现
#include <stdio.h>
#include <memory>
#include <vector>
class CContext;
class CState {
public:
virtual void DoAction(CContext* context) = 0;
virtual void ToString() = 0;
};
class CStartState : public CState {
public:
void DoAction(CContext* context);
void ToString() override {
std::cout << "This start state\n" << std::endl;
}
};
class CEndState : public CState {
public:
void DoAction(CContext* context);
void ToString() override {
std::cout << "This is end state\n" << std::endl;
}
};
class CContext {
public:
void SetState(CState* state) {
this->m_state = state;
}
CState* GetState(void)
{
return m_state;
}
protected:
CState* m_state;
};
void CStartState::DoAction(CContext* context){
std::cout << "Play is start" << std::endl;
context->SetState(this);
}
void CEndState::DoAction(CContext* context) {
std::cout << "Play is stop" << std::endl;
context->SetState(this);
}
#include <stdio.h>
#include <iostream>
#include <string>
#include "game.h"
int main(int argc, char** argv)
{
CContext* context = new CContext();
CStartState startState;
startState.DoAction(context);
context->GetState()->ToString();
CEndState endState;
endState.DoAction(context);
context->GetState()->ToString();
system("pause");
return 0;
}