Bootstrap

状态模式——C++实现

目录

1. 状态模式简介

2. 代码示例

3. 单例状态对象

4. 状态模式与策略模式的辨析


1. 状态模式简介

状态模式是一种行为型模式

状态模式的定义:状态模式允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。

通俗的说就是一个对象在不同的状态下拥有不同的行为。对象可以拥有多个不同的状态,不同状态下调用同一个接口会产生不同的行为。状态模式通过把状态封装成类,可以很好地维护一个对象的不同状态,并且方便地扩展新的状态。


举个例子:

假如在游戏程序中,要模拟一个怪物。怪物的总血量为100,当血量大于50时,怪物处于亢奋状态,怪物受击时会进行反击;当血量小于等于50时,怪物处于恐慌状态,受击时会选择逃跑。

在这个例子中,怪物具有明确的状态,并且在不同的状态下,同一个接口会有不同的行为。这种情况下就非常适合使用状态模式。

2. 代码示例

下面的代码实现了上文提到的怪物模拟程序:

#if 1

#include <iostream>

using namespace std;

class Monster;
class State
{
protected:
	Monster* monster_;
public:
	virtual ~State() {}
	State(Monster* m) :monster_(m) {}
	virtual void attacked(int damage) = 0;
};



class Monster
{
public:
	Monster();
	~Monster();
	void setState(State* state)
	{
		curState = state;
	}
	State* getExcitedState()
	{
		return excitedState;
	}
	State* getHorrifiedState()
	{
		return horrifiedState;
	}
	State* getDeadState()
	{
		return deadState;
	}
	void attacked(int damage)
	{
		curState->attacked(damage);
	}
	int getCurHealth()const { return health; }
	int reduceHealth(int damage)
	{
		health -= damage;health = max(0, health);
		return health;
	}
private:
	State* curState;
	State* excitedState;
	State* horrifiedState;
	State* deadState;
	int health = 100;
};

class ExcitedState : public State
{
public:
	using State::State;
	void attacked(int damage)
	{
		cout << "怪物的当前状态为 ExcitedState" << endl;
		cout << "怪物尝试反击" << endl;
		cout << "怪物受到" << damage << "点伤害" << endl;
		int curHealth = monster_->reduceHealth(damage);
		cout << "怪物剩余血量:" << curHealth << endl;
		if (curHealth <= 50)
		{
			monster_->setState(monster_->getHorrifiedState());
			cout << "怪物进入 HorrifiedState" << endl;
		}
	}
};

class HorrifiedState : public State
{
public:
	using State::State;
	void attacked(int damage)
	{
		cout << "怪物的当前状态为 HorrifiedState" << endl;
		cout << "怪物尝试逃跑" << endl;
		cout << "怪物受到" << damage << "点伤害" << endl;
		int curHealth = monster_->reduceHealth(damage);
		cout << "怪物剩余血量:" << curHealth << endl;
		if (curHealth <= 0)
		{
			monster_->setState(monster_->getDeadState());
			cout << "怪物进入 DeadState" << endl;
		}
	}
};

class DeadState : public State
{
public:
	using State::State;
	void attacked(int damage)
	{
		cout << "怪物的当前状态为 DeadState" << endl;
		cout << "怪物已经死亡, 无法受到伤害" << endl;
	}
};

Monster::Monster()
{
	excitedState = new ExcitedState(this);
	horrifiedState = new HorrifiedState(this);
	deadState = new DeadState(this);
	curState = excitedState;
}

Monster::~Monster()
{
	delete excitedState;
	delete horrifiedState;
	delete deadState;	
}


int main()
{
	Monster monster;
	monster.attacked(0);
	cout << "---------------------" << endl;
	monster.attacked(30);
	cout << "---------------------" << endl;
	monster.attacked(40);
	cout << "---------------------" << endl;
	monster.attacked(80);
	cout << "---------------------" << endl;
	monster.attacked(100);
	cout << "---------------------" << endl;
	return 0;
}

#endif 

运行结果如下图所示:

 

代码中增加了死亡状态,当怪物血量小于0时进入了死亡状态。

采用状态模式可以很方便地扩展,增加新的状态会非常便捷。

3. 单例状态对象

当状态对象不含有自身内部的状态时,可以考虑将状态类实现成单例模式

4. 状态模式与策略模式的辨析

状态模式策略模式的结构十分类似,两种模式的差别在于它们的目的不同。

  • 对于状态模式来说,可以将对象的一组行为封装在一个状态对象中,context对象(拥有状态的对象)的行为可以随时通过切换不同的状态对象而改变。随着时间的流逝,当前的状态也在多个状态对象中发生改变,以反映context内部的状态,context的行为也会改变。但是对于客户端代码对于context的状态对象可以毫无察觉,不需要了解。context的状态切换是在其内部进行的,客户端代码感知不到客户端只能感受到context的行为产生了变化,而无需了解其当前的状态是怎么样的
  • 对于策略模式来说,通常需要客户端主动指定context当前所需要的策略对象是哪一个。策略模式允许在程序运行过程中改变策略,但是对于某个context对象来说,通常只有一个当前最适合的策略。策略的切换是通过客户端代码主动发起的

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;