State模式对应到C++的多态特性。
State模式适用较广,这儿给出比较常见易懂的两种情况:
1. 当怪物在面对不同职业和特性的玩家时对应不同的AI处理和技能释放:
CSkill* pAttackSkill;
if( pPlayer->MagicImmune() ) pAttackSkill = SomePhysicalAttackSkill;
else if( pPlayer->PhysicalImmune() ) pAttackSkill = SomeMagicAttackSkill;
…
pAttackSkill->Begin();
…
或者使用分支结构:
CSkill* pAttackSkill;
switch( pPlayer->GetOccupation() )
{
case WARRIOR: pAttackSkill = SomeLongRangeSkill; break;
case MAGICIAN: pAttackSkill = SomeForceSkill; break;
case NIMROD: pAttackSkill = SomeMagicSkill; break;
…
}
pAttackSkill->Begin();
…
2. 我们也可以封装一个显式的转换接口:
CSkill* pAttackSkill;
pMonster->ChangeState(pPlayer->GetOccupation(), PAttackSkill);
pMonster->Attack();
…
这儿的CSkill类就是所谓的State,作为怪物的一个行为被封装,并因为各种上下文的不同而被赋予不同的实例。
上面提到的是State模式的一些使用方法并不严谨,却可以为我们探究State模式的特性打下很好的一个基础。
既然称为状态模式,我们可以将其与状态机联系起来,二者的共同之处在于状态转换,状态模式中各状态之间即存在一定的转换关系,这种转换关系可以像上面的例子那样放在上下文中,也可以放到各状态中。仍以怪物AI为例,一般情况下怪物的状态转换关系如下:
Init->Find->Attack->Defence->Hurt->Dead->Relive
这些状态的某些状态之间存在多种转换,但即使是多种转换也是可以确定的,比如一个怪物只有活着并且没有被攻击(我们认为被攻击时敌人就是攻击者,不需要附加寻找状态,虽然攻击者的确定可能会复杂些)的情况下才会寻找敌人,如果怪物不是被攻击(我们认为被驯服、被诅咒、被染病都属于被攻击)的话,不会防御、受伤和死亡(被服务器Kill掉例外),如果怪物没有死亡的话也不会被复活,等等。
这样一来,状态的转换就可以放到各具体状态内部去触发,这更符合状态模式的意图本身,而状态在内部还是外部被触发和转换是没有太大区别的,因此上面讲到的例子还是可以说明这一点。
其他应用包括像:
根据服务器的不同状态确定客户端的不同行为转换:Create->Bind->Listen->Accept->Recv->Send;
根据用户的不同操作实现特定的效果或行为转换等:Select->Attack->Gain->Hurt->Dead->Relive。
状态模式本身并不复杂,抽象出各状态及其转换关系并设计出完整、合理、易扩展的模型更重要。
在学习设计模式时,也有这样一个状态模型:
读懂模式的结构->思考模式的意图->构建模式的模型->拓展模式的应用
之所以看过会忘,也是因为你对这样一个状态转换还没有达到完整、合理、扩展的要求。
相关链接:
设计模式(一): http://www.yulefox.com/index.php/20080806/design-patterns-01.html/
Wikipedia: http://en.wikipedia.org/wiki/State_pattern
Source Making: http://sourcemaking.com/design_patterns/state
Data & Object Factory: http://www.dofactory.com/Patterns/PatternState.aspx
最近一直在加班,同事在看我Blog的时候,说我的设计模式只写了一篇就没了下文。我不好意思的解释说忙,系列的文章写起来累,而且我对设计模式说不上融会贯通,怕误导别人,也怕被骂。
但这个系列的确是我非常想写完的一个系列,所以就在这儿补上,慢慢补吧,毕竟欠下的还多的很。
但为了节省时间,我能不写代码就不写代码了,尽量把模式介绍清楚就是了。
标签: 开发, 模式
Fox @ 2008-11-19, 00:53:32 @ 模式架构, 403次浏览
引用通告:http://www.yulefox.com/index.php/20081119/design-patterns-02.html/trackback/