设计模式 - 状态模式

来源:互联网 发布:jenkins windows 部署 编辑:程序博客网 时间:2024/05/21 06:49

State Pattern,状态模式。个人用的蛮多的一种模式。

意图

允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。

结构

结构图还是蛮简单的,主要涉及两个类,context和state。state类会处理一些行为。状态模式的一个重要的特征就是:把和状态相关的行为都放入一个对象中。因为所有和状态相关的代码都存在于某一个state子类中,所以通过定义新的子类可以很容易地增加新的状态和转换。

 

举个例子吧,小弟我以前做过一个简单的跑酷类游戏。路上有一些障碍物,然后主角可以改变状态比如:普通的跑步状态,骑上飞龙后的飞行状态,吃下无敌药丸后的无敌状态等等。假如是普通状态,碰到一个障碍物,就挂了。如果是无敌状态,可以直接撞开障碍物。这个例子是蛮适合状态模式的。

1. 首先主角就是一个context;

2. 主角内部有一个状态,这个状态用来表示普通状态,飞行状态,无敌状态等等。不同的状态碰到障碍物时有不同的表现。也就是当主角内部状态改变的时候,主角会有不同的行为。

写一些代码来模拟一下吧。模拟代码就实现两种状态:普通状态和无敌状态。

先看看主角类吧,现在大家都倾向于面向接口编程,所以给个主角类的接口类,如:

class CRoleContext{public: virtual void ChangeState(CState* state) = 0;};

里面只提供一个接口函数,ChangeState,状态类可以通过这个函数来改变当前context的状态。

状态类接类定义:

class CState{public:virtual void CollideWithObstacle(CRoleContext* role) = 0;//处理和障碍物碰撞的行为};

这个状态类只处理一种行为,就是主角和障碍物碰撞的行为。具体有子类来实现。看看两种状态的实现:

class CNormalState: public CState{public:static CState* GetInst(){static CNormalState state;return &state;}void CollideWithObstacle(CRoleContext* role){cout << "you dead\n";}private:CNormalState(){};};class CInvincibleState: public CState{public:static CState* GetInst(){static CInvincibleState state;return &state;}void CollideWithObstacle(CRoleContext* role){cout << "no problem, you are on invincible state\n";cout << "change to normal state\n";role->ChangeState(CNormalState::GetInst());}private:CInvincibleState(){};};

实现了函数CollideWithObstacle,在普通状态下,主角碰到障碍就挂了。无敌模式下,碰到障碍,没事。不过碰到一个障碍后就把context(主角)的状态改成普通的了。也就是无敌状态只能撞开一个障碍,然后主角又变回普通状态了。这只是一个测试例子,我们可以根据实际情况来决定状态的变化。

看看主角类的具体实现:

class CRole: public CRoleContext{public:CRole(){m_state = CNormalState::GetInst();//默认使用普通状态}void ChangeState(CState* state){m_state = state;}void Collide(){m_state->CollideWithObstacle(this);}protected:CState* m_state;};

默认给定一个普通状态。然后当主角发生碰撞的时候,主角类不会处理碰撞,而是将碰撞交由状态类来处理。这就是状态模式的一个重要特征,前面已经讲过了。就是:将和状态相关的行为放入到状态类中,交由状态类来处理。OK,看看具体的调用:

void Pattern_State(){CRole* role = new CRole();//先模拟一个主角。role->Collide();//碰撞,结果就是挂了role->ChangeState(CInvincibleState::GetInst());role->Collide();//无敌状态,碰撞,没事。无敌状态的类里面会把状态再次改回普通状态。role->Collide();//又挂了,因为现在已经是普通状态了。}

运行一下,可以看到:

这就是状态模式的一个简单应用。

状态模式有如下几个特点:

1. context将和状态相关的行为交由状态类处理。context只是调用某个状态子类的行为。这也就是为什么context内部状态改变的时候context会有不同的行为。

2. 状态类通常是一个singleton,因为通常一种状态只有一种处理。这样singleton是比较合适的,当然如果状态还涉及到上下文的话,那么就不要对状态类使用singleton了。

3. 谁来切换context的状态。根据GOF的说法,状态模式并没有规定谁来切换状态。通常context和state类都可以切换状态。就像上面的例子里面,context默认使用普通跑步状态,然后无敌状态每当碰到一个障碍物的时候自动切换成普通状态。具体视情况而定。

4. 每次新建状态对象,用完就销毁呢?还是事先建立几个状态对象,就像本文的例子一样。各有各的好处吧。也是看具体情况。通常如果状态类有大量的信息,并且状态切换频繁,那么用第二种吧,不然创建和销毁会占用一定开销的。当然第二种方式有些时候也会浪费资源,因为状态对象一直存在。

 

好了,就讲这么多了。下次介绍类似的策略模式。从结构图看状态模式和策略模式几乎一模一样的,但是它们的意图又完全不同。

 

原创粉丝点击