想把c++的设计模式写完,就从状态机开始吧
状态机起源于我们在不同的状态下去做不同或相同的事情,会得到不同的结果
当然,这样是最直接的
switch(state)
case State1:
dosomething..
case State2:
dosomething..
......
这样是一定能把所有case写完的,就像计算器起源于0和1一样简单
但我们在实际工作中却不会这么做,主要是这么做扩展性不强,随着case增多,state1到state2到state3等等之间存在茫茫多的联系,在不同state下将产生越来越多的判断,最终写着写着就崩溃了。
c++用状态机去控制它们,要构建这样一个状态机,需要两个元素,一个是controller,控制状态的切换,统一管理这些状态,另外就是状态
设想这样的一个状态机,主体是人(controller),拥有3个健康状态 :
typedef enum
{
health ,
sub_health,
hopeless
} PERSON_STATE_ID;
这个人有三个爱好:
typedef enum
{
run = 0,
work,
latesleep
} PERSON_FAN;
这个人的类的构建:
class State;
class healthState;
class sub_healthState;
class hopelessState;
class Person
{
public:
Person(string name);
~Person();
void gotohealth();
void gotoSubHealth();
void gotoHopeless();
State* getCurrentState();
void setCurrentState(State * state);
State* getCurrentStatebyId(PERSON_STATE_ID id);
void init();
private:
string mName;
State * m_CurrentState;
healthState * mhealthState;
sub_healthState * msub_healthState;
hopelessState * mhopelessState;
};
它的数据成员有一个名字,一个当前状态的指针,指向健康状态的指针,指向非健康状态的指针和指向hopeless的指针。
接着构建健康三种状态的基类。
class State
{
public:
State(Person * Per):P_person(Per),mStateNum(health){};
virtual void behavior(PERSON_FAN num)= 0;
virtual void into()
{
cout<<"now state is: "<< getstateStringbyId(mStateNum).c_str()<< endl <<endl;
};
string getstateStringbyId(PERSON_STATE_ID Num)
{
switch(Num)
{
case health: return "health";
case sub_health: return "not health";
case hopeless: return "can not save yourself!";
}
}
Person* obtainPersonObject() const { return P_person; };
PERSON_STATE_ID getStateNum() const {return mStateNum;};
void setStateNum(PERSON_STATE_ID stateId) {mStateNum = stateId;};
virtual ~State(){};
private:
Person * P_person;
PERSON_STATE_ID mStateNum;
};
基类state拥有一个controller,即指向这个人的指针,和一个标记此时状态的数字id,另外做了一个纯虚函数去让不同状态进行切换,做几个也可以,这个不限
几个基本健康状态的类,当这个人在健康时,如果天天work,就会进入sub_health,如果天天 latesleep,就会进入hopeless的状态,而如果在hopeless的状态,天天run也只能进入sub_health的状态
class healthState : public State
{
public:
healthState(Person* mPerson): State(mPerson){};
~healthState(){};
void behavior(PERSON_FAN num)
{
switch(num)
{
case run:
{}
break;
case work:
{
obtainPersonObject()->gotoSubHealth();
}
break;
case latesleep:
{
obtainPersonObject()->gotoHopeless();
}
break;
default:
break;
}
}
void into()
{
setStateNum(health);
State::into();
}
};
class sub_healthState : public State
{
public:
sub_healthState(Person* mPerson): State(mPerson){};
~sub_healthState(){};
void behavior(PERSON_FAN num)
{
switch(num)
{
case run:
{
obtainPersonObject()->gotohealth();
}
case work:
{
//do nothing;
}
break;
case latesleep:
{
obtainPersonObject()->gotoHopeless();
}
break;
default:
break;
}
}
void into()
{
setStateNum(sub_health);
State::into();
}
};
class hopelessState : public State
{
public:
hopelessState(Person* mPerson):State(mPerson){};
~hopelessState(){};
void behavior(PERSON_FAN num)
{
switch(num)
{
case run:
{
obtainPersonObject()->gotoSubHealth();
}
break;
case work:
case latesleep:
{
//do nothing;
}
break;
default:
break;
}
}
void into()
{
setStateNum(hopeless);
State::into();
}
};
人这个controller的具体实现:
Person::Person(string name):mName(name)
{
init();
};
Person::~Person()
{
delete mhealthState;
delete msub_healthState;
delete mhopelessState;
};
void Person::gotohealth()
{
mhealthState->into();
setCurrentState(mhealthState);
};
void Person::gotoSubHealth()
{
msub_healthState->into();
setCurrentState(msub_healthState);
};
void Person::gotoHopeless()
{
mhopelessState->into();
setCurrentState(mhopelessState);
};
State *Person::getCurrentState()
{
return m_CurrentState;
};
void Person::setCurrentState(State * state)
{
m_CurrentState = state;
};
void Person::init()
{
mhealthState = new healthState(this);
msub_healthState = new sub_healthState(this);
mhopelessState = new hopelessState(this);
m_CurrentState = mhealthState;
};
状态的迁移实际是通过m_CurrentState这个指针来完成的,当然基于C++那个封装的特性,这么写肯定是不行的,声明和实现得分开到.cpp和.h中,我这么写只为编过
void main()
{
Person mperson("common");
int a;
cout<<"please input your favorite id(0: run; 1: work; 2: latesleep):"<<endl;
while(cin>>a)
{
mperson.getCurrentState()->behavior(PERSON_FAN(a));
cout<<"please input your favorite id(0: run; 1: work; 2: latesleep):"<<endl;
}
}
主函数入口,前面省略了几个基本头文件和不推荐使用的using namespace std;运行结果:
还是顺便练习一下.