状态模式

来源:互联网 发布:老公拔出来 知乎 编辑:程序博客网 时间:2024/06/05 09:13

当一个对象的行为取决于具体的状态时,往往在代码实现中还包括大量的条件分支语句,这时候根据面向对象原则,我们应该封装状态,也就是状态模式。

优势和缺陷:

状态模式的好处是可以让我们任意的增加状态而不用修改其他代码,使得代码逻辑比较清晰,坏处和很多设计模式一样,会增加类的个数,如果使用不当会使项目的结构变得复杂,所以要明确需求和需求的可能性变化,适当使用。

一个通用的例子:

借用网上别人的代码举一个电梯运行的例子(详见点击打开链接)。

电梯有多种电梯,可以处于运行状态,停止状态,开门状态,关门状态。

abstract class ILift {  //电梯的四个状态  const OPENING_STATE = 1;  //门敞状态  const CLOSING_STATE = 2;  //门闭状态  const RUNNING_STATE = 3;  //运行状态  const STOPPING_STATE = 4; //停止状态;  //设置电梯的状态  public abstract function setState($state);  //首先电梯门开启动作  public abstract function open();  //电梯门有开启,那当然也就有关闭了  public abstract function close();  //电梯要能上能下,跑起来  public abstract function run();  //电梯还要能停下来,停不下来那就扯淡了  public abstract function stop();  }  /**  * 电梯的实现类   */   class Lift extends  ILift {  private $state;  public function setState($state) {  $this->state = $state;  }  //电梯门关闭  public function close() {  //电梯在什么状态下才能关闭  switch($this->state){  case ILift::OPENING_STATE:  //如果是则可以关门,同时修改电梯状态  $this->setState(ILift::CLOSING_STATE);  break;  case ILift::CLOSING_STATE:  //如果电梯就是关门状态,则什么都不做  //do nothing;  return ;  break;  case ILift::RUNNING_STATE: //如果是正在运行,门本来就是关闭的,也说明都不做  //do nothing;  return ;  break;  case ILift::STOPPING_STATE:  //如果是停止状态,本也是关闭的,什么也不做  //do nothing;  return ;  break;  }  echo 'Lift colse <br>';  }  //电梯门开启  public function open() {  //电梯在什么状态才能开启  switch($this->state){  case ILift::OPENING_STATE: //如果已经在门敞状态,则什么都不做  //do nothing;  return ;  break;  case ILift::CLOSING_STATE: //如是电梯时关闭状态,则可以开启  $this->setState(ILift::OPENING_STATE);  break;  case ILift::RUNNING_STATE: //正在运行状态,则不能开门,什么都不做  //do nothing;  return ;  break;  case ILift::STOPPING_STATE: //停止状态,淡然要开门了  $this->setState(ILift::OPENING_STATE);  break;  }  echo 'Lift open <br>';  }  ///电梯开始跑起来  public function run() {  switch($this->state){  case ILift::OPENING_STATE: //如果已经在门敞状态,则不你能运行,什么都不做  //do nothing;  return ;  break;  case ILift::CLOSING_STATE: //如是电梯时关闭状态,则可以运行  $this->setState(ILift::RUNNING_STATE);  break;  case ILift::RUNNING_STATE: //正在运行状态,则什么都不做  //do nothing;  return ;  break;  case ILift::STOPPING_STATE: //停止状态,可以运行  $this->setState(ILift::RUNNING_STATE);  }  echo 'Lift run <br>';  }  //电梯停止  public function stop() {  switch($this->state){  case ILift::OPENING_STATE: //如果已经在门敞状态,那肯定要先停下来的,什么都不做  //do nothing;  return ;  break;  case ILift::CLOSING_STATE: //如是电梯时关闭状态,则当然可以停止了  $this->setState(ILift::CLOSING_STATE);  break;  case ILift::RUNNING_STATE: //正在运行状态,有运行当然那也就有停止了  $this->setState(ILift::CLOSING_STATE);  break;  case ILift::STOPPING_STATE: //停止状态,什么都不做  //do nothing;  return ;  break;  }  echo 'Lift stop <br>';  }  }  $lift = new Lift();   //电梯的初始条件应该是停止状态   $lift->setState(ILift::STOPPING_STATE);   //首先是电梯门开启,人进去   $lift->open();   //然后电梯门关闭   $lift->close();   //再然后,电梯跑起来,向上或者向下   $lift->run();      //最后到达目的地,电梯挺下来   $lift->stop();  


状态模式的基本结构:


环境类(Context):  这个类描述具体的环境,调用各种状态。
抽象状态类(State):  定义所有状态的公有部分。
具体状态类(ConcreteState):  描述具体的状态行为。

/**  *   * 定义一个电梯的接口   */   abstract class LiftState{        //定义一个环境角色,也就是封装状态的变换引起的功能变化      protected  $_context;        public function setContext(Context $context){          $this->_context = $context;      }        //首先电梯门开启动作      public abstract function open();        //电梯门有开启,那当然也就有关闭了      public abstract function close();        //电梯要能上能下,跑起来      public abstract function run();        //电梯还要能停下来,停不下来那就扯淡了      public abstract function stop();    }      /**  * 环境类:定义客户感兴趣的接口。维护一个ConcreteState子类的实例,这个实例定义当前状态。  */   class Context {      //定义出所有的电梯状态      static  $openningState = null;      static  $closeingState = null;      static  $runningState  = null;      static  $stoppingState = null;        public function __construct() {          self::$openningState = new OpenningState();          self::$closeingState = new ClosingState();          self::$runningState =  new RunningState();          self::$stoppingState = new StoppingState();        }        //定一个当前电梯状态      private  $_liftState;        public function getLiftState() {          return $this->_liftState;      }        public function setLiftState($liftState) {          $this->_liftState = $liftState;          //把当前的环境通知到各个实现类中          $this->_liftState->setContext($this);      }          public function open(){          $this->_liftState->open();      }        public function close(){          $this->_liftState->close();      }        public function run(){          $this->_liftState->run();      }        public function stop(){          $this->_liftState->stop();      }  }    /**  * 在电梯门开启的状态下能做什么事情   */   class OpenningState extends LiftState {        /**      * 开启当然可以关闭了,我就想测试一下电梯门开关功能      *      */      public function close() {          //状态修改          $this->_context->setLiftState(Context::$closeingState);          //动作委托为CloseState来执行          $this->_context->getLiftState()->close();      }        //打开电梯门      public function open() {          echo 'lift open...', '<br/>';      }      //门开着电梯就想跑,这电梯,吓死你!      public function run() {          //do nothing;      }        //开门还不停止?      public function stop() {          //do nothing;      }    }    /**  * 电梯门关闭以后,电梯可以做哪些事情   */   class ClosingState extends LiftState {        //电梯门关闭,这是关闭状态要实现的动作      public function close() {          echo 'lift close...', '<br/>';        }      //电梯门关了再打开,逗你玩呢,那这个允许呀      public function open() {          $this->_context->setLiftState(Context::$openningState);  //置为门敞状态          $this->_context->getLiftState()->open();      }        //电梯门关了就跑,这是再正常不过了      public function run() {          $this->_context->setLiftState(Context::$runningState); //设置为运行状态;          $this->_context->getLiftState()->run();      }        //电梯门关着,我就不按楼层            public function stop() {          $this->_context->setLiftState(Context::$stoppingState);  //设置为停止状态;          $this->_context->getLiftState()->stop();      }    }    /**  * 电梯在运行状态下能做哪些动作   */   class RunningState extends LiftState {        //电梯门关闭?这是肯定了      public function close() {          //do nothing      }        //运行的时候开电梯门?你疯了!电梯不会给你开的      public function open() {          //do nothing      }        //这是在运行状态下要实现的方法      public function run() {          echo 'lift run...', '<br/>';      }        //这个事绝对是合理的,光运行不停止还有谁敢做这个电梯?!估计只有上帝了      public function stop() {          $this->_context->setLiftState(Context::$stoppingState); //环境设置为停止状态;          $this->_context->getLiftState()->stop();      }    }        /**  * 在停止状态下能做什么事情   */   class StoppingState extends LiftState {        //停止状态关门?电梯门本来就是关着的!      public function close() {          //do nothing;      }        //停止状态,开门,那是要的!      public function open() {          $this->_context->setLiftState(Context::$openningState);          $this->_context->getLiftState()->open();      }      //停止状态再跑起来,正常的很      public function run() {          $this->_context->setLiftState(Context::$runningState);          $this->_context->getLiftState()->run();      }      //停止状态是怎么发生的呢?当然是停止方法执行了      public function stop() {          echo 'lift stop...', '<br/>';      }    }    /**  * 模拟电梯的动作   */   class Client {        public static function main() {          $context = new Context();          $context->setLiftState(new ClosingState());            $context->open();          $context->close();          $context->run();          $context->stop();      }  }  Client::main();  

Unity的例子:

状态模式最好的例子就是Unity的动画状态机,比如说我们有走和攻击的动画。

public  class  NPCAnimal:MonoBehaviour{Animator  animator ;public  void  PlayAttack(){Vector3 tmpPos = transform.position;tmpPos.x += 10;animator.SetInteger ("npcState",1);}public  void PlayWalk(){transform.Translate (transform.forward);animator.SetInteger ("npcState",3);}}

using UnityEngine;using System.Collections;public  abstract  class   FSMState{private   byte  stateId ;//进入这个状态之前 干一些事public  virtual  void OnBeforEntor (){}//切换状态要 复制一些 东西public  virtual   void CopyState(){}//进入状态时要干的事,每个状态必须重写public  abstract  void  OnEnter () ;//update函数里要干的事public  virtual void Update(){}//离开这个状态 会干一些事情public   virtual  void OnLeave(){}}//状态管理系统,负责增加状态和切换状态public  class   FSMManager{private   FSMState[] fsmManager;// 增加了几个stateprivate   byte  curAdd ;public  byte   curStateId ;public  FSMManager (byte  MaxNumber){curAdd = 0;curStateId = 0;fsmManager = new FSMState[MaxNumber];}public  void AddState(FSMState  tmpState){if (curAdd < fsmManager.Length) {fsmManager [curAdd] = tmpState;curAdd++;}}public  void ChangeState(byte  stateId){fsmManager [curStateId].OnLeave ();fsmManager [stateId].CopyState (fsmManager [curStateId]);fsmManager [stateId].OnBeforEntor ();fsmManager [stateId].OnEnter ();curStateId = stateId;}public  void Update(){fsmManager [curStateId].Update ();}}//攻击状态public   class  AtackFSM: FSMState{ParticleEmitter  emmitor ;public override void OnEnter (){//emmitor.}float  timeCount =0 ;public  override void Update(){timeCount += Time.deltaTime;if (timeCount > 0.5f) {}}}//走路状态public   class  WalkFSM: FSMState{public override void OnEnter (){// animator.setint()}}//环境类,具体调用状态,是通过状态管理机去调用的public class FSM : MonoBehaviour {public enum   FSMStateId{Idle,Attack,Walk,MaxValue}FSMStateId   stateId ;FSMManager   manager ;// Use this for initializationvoid Start () {WalkFSM tmpWalk = new WalkFSM ();AtackFSM tmpAttack = new AtackFSM ();manager = new FSMManager (FSMStateId.MaxValue);manager.AddState (tmpWalk);manager.AddState (tmpAttack);}public  void PlayAttack(){manager.ChangeState (FSMStateId.Attack);}// Update is called once per framevoid Update () {manager.Update ();}}


0 0
原创粉丝点击