设计模式:状态机模式

首先状态机模式是处理一个类在内部状态改变的时候,其方法处理信息的模式也会改变;

这里说一个在RTS游戏里的应用:有限状态机。

我们要赋予每个战斗单位一个智能,比如一定范围内检测到地方单位,且自身处于游荡或者Patrol状态,那么就转换为攻击状态,再比如我们给这个单位一个通往目的地的路径,那么这个单位会自动前往目的地,如果到达目的地,那么就进入Idle状态。

这里我们可以写一个状态管理类:

class StateSystem{
    void State state;
public:
    void Run(){
        if(state==State::Idle){
            //...       
            if(传递路径){
                LeaveState();
                state==State::Path;
                 InitState();
            }
        }else if(state==State::Path){
            //...
            if(//到达终点){
                 LeaveState();
                 state=State::Idle;
                 InitState();
            }
        }else if(state==State::Patrol){
            //...
            if(周边有敌人){
                 LeaveState();
                state=State::Attack;
                 InitState();
            }
        }else if(state==State::Attack){
            //...
            if(敌人被消灭){
                LeaveState();
                state=Satte::Idle;
                 InitState();
            }
        }
    }
    
    void InitState(){
        if(state==State::Idle){
            //...       
        }else if(state==State::Path){
            //...
        }else if(state==State::Patrol){
            //...
        }else if(state==State::Attack){
            //...
        }
    }

    void LeaveState(){
        if(state==State::Idle){
            //...       
        }else if(state==State::Path){
            //...
        }else if(state==State::Patrol){
            //...
        }else if(state==State::Attack){
            //...
        }
    }
}

可以看到,典型的bad smell,这里我们可以将每个状态抽象出来,设计好接口,然后用多态来替代if_else。

这个就是状态机模式的精髓,因为处理的问题就是一个类的方法处理信息的模式和类的状态有关,那么换句话说技术类中的每个状态都要处理相同的信息,那么我们为什么不把这种状态抽离出来呢,然后单独考虑呢?这样我们可以在原本的类中存储这些类的抽象接口,然后通过接口来调用这些状态。

看以下代码:

#include

enum State {
	Idle,Patrol,Path,Attack,None
};

class State_Ifc {
	State state;
public:
	virtual void Init() = 0;//初始化状态
	virtual void Leave() = 0;//卸载状态
	virtual void Run() = 0;//处理信息
	virtual State Reason() = 0;//转移到其他状态
	virtual State GetState() { return state; }
};

class IdleState :public State_Ifc {
	//....重写
};

class PatrolState :public State_Ifc {
	//....重写
};

class PathState :public State_Ifc {
	//....重写
};

class AttackState :public State_Ifc {
	//....重写
};

class StateSystem {
	State_Ifc* Current;//当前的状态
	std::unordered_map map;
	//
public:
	void UpData() {
		Current->Run();
	}
	void Trans() {
		State temp = Current->Reason();
		if (temp != State::None) {
			Current->Leave();
			Current = map[temp];
			Current->Init();
		}
	}
};

这样子的话,State不依赖于具体的状态,而是依赖于抽象接口,这样子的话,就为拓展带来了可能。

但是,这个模式也有坏处,那就是没当添加新的状态,如果这个状态有和其他状态有转换,那么我们就不得不修改源代码,这里,本菜想,可以通过中介者模式来解决这个问题,具体方法是将
Reason方法从State子类中抽离出来,这样,我们修改转换时,只需要修改Mediator类即可:

具体如下:

#include
#include
enum State {
	Idle,Patrol,Path,Attack,None
};

class Mediator_Ifc {
public:
	virtual State Trans(State stateS, State stateE) = 0;
};

class State_Ifc {
	std::list list;
	State state;
	Mediator_Ifc* Mediator;
public:
	virtual void Init() = 0;//初始化状态
	virtual void Leave() = 0;//卸载状态
	virtual void Run() = 0;//处理信息
	virtual State Reason() {
		for (auto t : list) {
			State temp = Mediator->Trans(state, t);
			if (temp != State::None) {
				return temp;
			}
		}
		return State::None;
	}
	virtual State GetState() { return state; }
};

class IdleState :public State_Ifc {
	//....重写
};

class PatrolState :public State_Ifc {
	//....重写
};

class PathState :public State_Ifc {
	//....重写
};

class AttackState :public State_Ifc {
	//....重写
};


class MediatorCenter :public Mediator_Ifc {
	State Trans(State stateS, State stateE) {
		//...实现
	}
};

class StateSystem {
	Mediator_Ifc* Mediator;
	State_Ifc* Current;//当前的状态
	std::unordered_map map;
	//
public:
	void UpData() {
		Current->Run();
	}
	void Trans() {
		State temp = Current->Reason();
		if (temp != State::None) {
			Current->Leave();
			Current = map[temp];
			Current->Init();
		}
	}
};

这样就可以了。

总之一句话:状态机模式是处理一个类在内部状态改变的时候,其方法处理信息的模式也会改变;

你可能感兴趣的:(设计模式,状态模式)