小话设计模式(二十)状态模式

状态(State)模式,又名状态对象(Objects for States)模式,它允许一个对象在内部状态改变时改变它的行为。对象看起来似乎改变了它的类。

状态模式的应用场景:

1、一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变自己的行为。

2、一个操作中含有庞大的多分支的条件语句,而这些分支条件依赖于对象的状态。

Unity3D里面有一个很有意思的东西,叫做AnimatorController,它可以根据用户设置的条件和参数的不同表现出不同的动画,例如设定速度为0为待机动作,小于5为行走动作,大于5为跑步动作。虽然具体实现不得而知,但无疑这是状态模式的一种应用。

本文要举这么一个例子:

有这样一个游戏,主角身体里住着三个灵魂,它可以切换成对应的三种形态,来施放不同的技能。(这与小话设计模式(七)桥接模式是同一个例子,文末会讨论他们的区别)

灵魂接口定义:

public interface ISoul
{
	void CastSkill(Hero hero);
}

灵魂的枚举类型定义:

public enum SoulState
{
	Mage,
	Hunter,
	Warrior,
}

英雄定义:

public class Hero
{
	ISoul _soul;
	public Hero()
	{
		_soul = Warrior.GetInstance ();
	}
	public SoulState soulState {get;set;}
	public void ChangeState(ISoul soul)
	{
		_soul = soul;
	}
	public void CastSkill()
	{
		_soul.CastSkill (this);
	}
}

可以看到,Hero的构造函数里面调用了Warrior类的GetInstance这个方法,熟悉设计模式的同学应该可以推测出,本文把灵魂定义为单例类。因为我们希望状态类的内部是要保存一些自身的数值的,例如在CastSkill之后,这个技能会进入CD,那么立刻切换灵魂,并立刻切换回来的时候,我们希望这个灵魂还在CD当中(CD足够长)。

本文借用了小话设计模式(一)单例模式的写法,来实现单例:

public class TSingleton<T> where T : class, new() {
	static T instance;
	static readonly object syncObj = new object();

	public static T GetInstance()
	{
		if (instance == null) {
			lock (syncObj) {
				if (instance == null) {
					instance = new T ();
				}
			}
		}
		return instance;
	}

	public static void PurgeInstance()
	{
		instance = null;
	}
}

每个灵魂的定义:

public class Mage : TSingleton<Mage>, ISoul
{
	public void CastSkill(Hero hero)
	{
		if (hero.soulState == SoulState.Hunter) {
			hero.ChangeState (Hunter.GetInstance ());
			hero.CastSkill ();
		} else if (hero.soulState == SoulState.Warrior) {
			hero.ChangeState (Warrior.GetInstance ());
			hero.CastSkill ();
		} else {
			Console.WriteLine ("Summon a box");
		}
	}
}

public class Hunter : TSingleton<Hunter>, ISoul
{
	public void CastSkill(Hero hero)
	{
		if (hero.soulState == SoulState.Mage) {
			hero.ChangeState (Mage.GetInstance ());
			hero.CastSkill ();
		} else if (hero.soulState == SoulState.Warrior) {
			hero.ChangeState (Warrior.GetInstance ());
			hero.CastSkill ();
		} else {
			Console.WriteLine ("Fire arrow");
		}
	}
}

public class Warrior : TSingleton<Warrior>, ISoul
{
	public void CastSkill(Hero hero)
	{
		if (hero.soulState == SoulState.Hunter) {
			hero.ChangeState (Hunter.GetInstance ());
			hero.CastSkill ();
		} else if (hero.soulState == SoulState.Mage) {
			hero.ChangeState (Mage.GetInstance ());
			hero.CastSkill ();
		} else {
			Console.WriteLine ("Power of Thor");
		}
	}
}

这里我们也可以加入一些其他的判断,例如英雄血量小于20的时候,必须切换为Warrior灵魂。

使用:

		Hero hero = new Hero ();
		hero.soulState = SoulState.Mage;
		hero.CastSkill ();


本文使用了小话设计模式(七)桥接模式例子,但是实现方法却不太相同。桥接模式是由Hero(Handle手)来完成切换Soul(Body体)的工作,而状态模式中Hero(Context环境)只是设置了一些参数(soulState或者hp),而Soul(State状态)自身负责完成切换的工作。

状态模式的优点:

1、它将特定状态下的相关行为局部化,将不同的状态行为分割开来。

2、它似的状态转换显式化。

3、State对象可以被共享,前提是State对象没有实例变量,即它们没有内部状态(本文示例不符合这个条件)。

缺点:

1、每个状态都会产生一个新类,这样会增加维护成本。

2、因为C#不存在友元,所以Hero的ChangeState方法不得不暴露出来,这样破坏了封装性(虽然友元本身也是破坏封装性的存在)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值