当前位置: > 财经>正文

趣谈设计模式 外汇市场总是遵循某一合乎逻辑的变动模式吗

2023-09-03 11:58:34 互联网 未知 财经

文章目录 案例:马里奥积分竞赛有限状态机分支逻辑法查表法 状态模式状态模式与策略模式总结完整代码与文档

案例:马里奥积分竞赛

喜欢马里奥的小伙伴们都应该知道,前不久马里奥为了庆祝35周年,推出了一款以多人对抗大逃杀为核心的超级马里奥兄弟35 此处仅为举例,并无此开发计划

由于新颖的游戏模式带来了巨大的热度,于是任天堂决定趁热打铁,推出一款马里奥竞技游戏,在一定时间内获得积分最多的玩家将获得胜利。考虑到游戏并非正传,于是任天堂将游戏的开发工作外包给了小明所在的游戏公司来进行制作。

游戏的核心玩法就是在一定时间内获取最高的积分,为了增加游戏的难度,我们设定只有获取道具才能够获得积分,而一旦遭受伤害就会损失积分,而死亡后积分就会清空。同时为了给落后的玩家反击的机会,以及给领先的玩家造成压迫感,玩家死亡后并不会退出游戏,而是积分清空后重新挑战。

在最初的版本中,我们只开放了蘑菇、太阳花两种道具,以及简单的设置了造成伤害的陷阱,于是马里奥的状态和行为如下 状态具有四种,分别是普通马里奥、超级马里奥、火焰马里奥、死亡马里奥

由于开放的道具不多,所以行为只有获得蘑菇、获得太阳花、受到伤害、复活四种。并且不同的行为都会带来不同的状态/分数变化。

根据状态和行为,画出状态转移图 从上面可以看出,如果我们要实现这些逻辑的转换,其实就是去实现一个状态机,为了照顾到不了解状态机的同学,下面我会简单的描述一下什么是状态机

有限状态机

有限状态机简写为FSM(Finite State Machine),我们通常将其简称为状态机。状态机由以下三个部分组成:状态(State)、事件(Event)、动作(Action),其中事件也被叫做转移条件(Transition Condition)

状态机的作用就是根据不同的事件来触发状态的转移以及动作的执行

例如上面提到的马里奥中的形态转变,就是一个状态机。其中马里奥的不同形态(如超级马里奥,火焰马里奥)就是状态机中的状态。游戏中触发的事件(获得蘑菇、遭受伤害)就是状态机中的事件。触发事件后的积分变化就是状态机的动作。而其中由事件(吃蘑菇)带来的状态变化(普通马里奥变为超级马里奥)就是状态转移。

那么我们如何用代码来实现上面所说的状态机呢?下面我会分别介绍三种方法,分别是分支逻辑法、查表法、状态模式

首先给出一个通用的自动机骨架,我们使用枚举来表示四种状态, 同时为状态机提供触发事件以及获取信息的接口,代码如下

//状态enum State{ NORMAL, //普通状态 SUPER, //超级状态 FIRE, //火焰状态 DEAD //死亡状态};//状态机class MarioStateMachine{public: MarioStateMachine() : _score(0) , _state(NORMAL) {} void getRevive(); //复活 void getMushroom(); //获得蘑菇 void getSunFlower(); //获得太阳花 void getHurt(); //受到伤害 int getScore() const; //获取当前分数 State getState() const; //获取当前状态private: int _score; //当前分数 State _state; //当前状态}; 分支逻辑法

要想实现状态机,最容易的方法就是直接参照状态转移图,直接将每种事件中每种状态变化翻译成代码,由于这样的代码中存在大量的分支逻辑判断,所以这种方法又叫做分支逻辑法

代码实现如下

//获取当前分数int MarioStateMachine::getScore() const{ return _score;}//获取当前状态State MarioStateMachine::getState() const{ return _state;}//复活void MarioStateMachine::getRevive(){ if(_state == FIRE) { std::cout std::cout if(_state == FIRE) { _score += 100; std::cout _state = SUPER; _score += 100; std::cout if(_state == FIRE) { _score += 200; std::cout _state = FIRE; _score += 200; std::cout if(_state == FIRE) { _state = SUPER; _score -= 100; std::cout _state = DEAD; _score = 0; std::cout MarioStateMachine Mario; Mario.getMushroom(); //马里奥获取蘑菇 Mario.getSunFlower(); //马里奥获取太阳花 Mario.getSunFlower(); //马里奥获取太阳花 std::cout public: MarioStateMachine() : _score(0) , _state(NORMAL) {} void getRevive(); //复活 void getMushroom(); //获得蘑菇 void getSunFlower(); //获得太阳花 void getHurt(); //受到伤害 int getScore() const; //获取当前分数 State getState() const; //获取当前状态private: void executeEvent(Event event); //执行事件 int _score; //当前分数 State _state; //当前状态 static std::vector _actionTable; //行为表 static std::vector _stateTable; //状态表};

填写转移表和行为表

//用INT_MIN表示清空,用0表示不符合逻辑的忽略情况std::vector MarioStateMachine::_actionTable = { {100, 200, INT_MIN, 0}, {100, 200, -100, 0}, {200, 200, -100, 0}, {0, 0, 0, INT_MIN},};std::vector MarioStateMachine::_stateTable = { {SUPER, FIRE, DEAD, NORMAL}, {SUPER, FIRE, NORMAL, SUPER}, {FIRE, FIRE, SUPER, FIRE}, {DEAD, DEAD, DEAD, NORMAL},};

接下来用查表法来实现我们的新骨架,我们提供了一个executeEvent接口,当执行各种事件函数的时候就会根据事件去查询表来获取结果

void MarioStateMachine::executeEvent(Event event){ int score = _actionTable[_state][event]; //查询表中对应的动作 _score = (score == INT_MIN) ? _score = 0 : _score + score; //如果为INT_MIN,则说明需要清空 _state = _stateTable[_state][event]; //查询表中对应的状态}void MarioStateMachine::getRevive(){ executeEvent(GET_REVIVE);} void MarioStateMachine::getMushroom(){ executeEvent(GET_MUSHROOM);}; void MarioStateMachine::getSunFlower(){ executeEvent(GET_SUNFLOWER);}; void MarioStateMachine::getHurt(){ executeEvent(GET_HURT);};

用上面的测试代码再次进行测试

int main(){ MarioStateMachine Mario; Mario.getMushroom(); //马里奥获取蘑菇 Mario.getSunFlower(); //马里奥获取太阳花 Mario.getSunFlower(); //马里奥获取太阳花 std::cout public: NormalMario(MarioStateMachine* stateMachine) : _stateMachine(stateMachine) {} void getRevive() override { std::cout _stateMachine->setScore(_stateMachine->getScore() + 200); _stateMachine->setState(_stateMachine->getFireMario()); std::cout return "普通马里奥"; }private: MarioStateMachine* _stateMachine; //状态机};

接着我们用策略模式来改写状态机,其中为了避免大量生成状态对象,我提前将所有状态缓存到状态机中,并提供获取状态实例的接口(我们也可以采用将状态与单例模式相结合的做法,保证每个状态只有一个实例),代码如下

class MarioStateMachine{public: MarioStateMachine() : _score(0) { //提前缓存各种状态 _normalMario = new NormalMario(this); _superMario = new SuperMario(this); _fireMario = new FireMario(this); _deadMario = new DeadMario(this); _state = _normalMario; } ~MarioStateMachine() { delete _normalMario, _superMario, _fireMario, _deadMario; } void getRevive(); //复活 void getMushroom(); //获得蘑菇 void getSunFlower(); //获得太阳花 void getHurt(); //受到伤害 int getScore() const; //获取当前分数 MarioState* getState() const; //获取当前状态 void setScore(int score); //获取当前分数 void setState(MarioState* state); //获取当前状态 MarioState* getNormalMario(); //获取缓存的状态 MarioState* getSuperMario(); MarioState* getFireMario(); MarioState* getDeadMario();private: int _score; //当前分数 MarioState* _state; //当前状态 MarioState* _superMario; //缓存所有的状态 MarioState* _normalMario; MarioState* _fireMario; MarioState* _deadMario;};

由于状态机会将事件发生后的行为与状态转移委托给当前的状态对象,因此我们只需要调用状态对象的方法即可

void MarioStateMachine::getRevive(){ _state->getRevive();}void MarioStateMachine::getMushroom(){ _state->getMushroom();}void MarioStateMachine::getSunFlower(){ _state->getSunFlower();}void MarioStateMachine::getHurt(){ _state->getHurt();} int MarioStateMachine::getScore() const{ return _score;}MarioState* MarioStateMachine::getState() const{ return _state;}void MarioStateMachine::setScore(int score) { _score = score;}void MarioStateMachine::setState(MarioState* state) { _state = state;}MarioState* MarioStateMachine::getNormalMario() { return _normalMario;} MarioState* MarioStateMachine::getSuperMario(){ return _superMario;}MarioState* MarioStateMachine::getFireMario(){ return _fireMario;}MarioState* MarioStateMachine::getDeadMario(){ return _deadMario;}

接着继续使用开头的代码进行测试,由于我们是在一开始搭建的状态机骨架上进行拓展的,因此不需要修改任何代码

int main(){ MarioStateMachine Mario; Mario.getMushroom(); //马里奥获取蘑菇 Mario.getSunFlower(); //马里奥获取太阳花 Mario.getSunFlower(); //马里奥获取太阳花 std::cout

版权声明: 本站仅提供信息存储空间服务,旨在传递更多信息,不拥有所有权,不承担相关法律责任,不代表本网赞同其观点和对其真实性负责。如因作品内容、版权和其它问题需要同本网联系的,请发送邮件至 举报,一经查实,本站将立刻删除。