状态模式
来源:互联网 发布:老公拔出来 知乎 编辑:程序博客网 时间: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
- 状态模式(状态变化)
- 状态模式
- 状态模式
- 状态模式
- 状态模式
- 状态模式
- 状态模式
- 状态模式
- 状态模式
- 状态模式
- 状态模式
- 状态模式
- 状态模式
- 状态模式
- 状态模式
- 状态模式
- 状态模式
- 状态模式
- Java 中 进制之间的转换
- 再次理解STM32中的堆栈机制
- 边界测试、健壮测试的测试用例个数:
- 仿微信群聊头像(图像合成、缩放)
- bootstrap fileinput 文件上传工具
- 状态模式
- java 发送邮件+附件
- JAVA各种系统架构图及其简介
- 欢迎使用CSDN-markdown编辑器
- 计算字符个数
- typeof和instanceof简介及用法
- java程序猿必修技能
- PPM和PWM
- 修改字体大小