状态模式(State) 坏的代码——冗长,难读,判断分支过多
如何避免一个程序中写满了if和else分支判断呢?
如果一个程序的判断分支过多时,代表着该程序的责任过多了,无论任何状态的改变,都通过它单独来改变,这是糟糕的。
面向对象设计其实就是希望做到代码的责任分解。
当一个对象的内在状态改变时,允许改变其行为,这个对象看起来像是改变了其类。
状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂的情况。把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化。
状态模式结构图
状态模式成员分析 Context上下文对象,该类维护一个ConcreteState子类的实例,表示当前的状态。 State抽象状态类,定义一个接口封装与Context特定状态相关的行为。 ConcreteState具体状态类,每一个子类实现一个与Context中一个状态相关的行为。
状态模式代码结构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 代码实现:abstract class State { public abstract void Handle (Context context ) ; }class ConcreteState1 : State { public override void Handle (Context context ) { context.State = new ConcreteStateB(); } }class ConcreteStateB : State { public override void Handle (Context context ) { context.State = new ConcreteState1(); } }class Context { private State state; public Context (State state ) { this .state = state; } public State State { get { return state; } set { state = value ; } } public void Request () { state.Handle(this ); } } Context context = new Context(new ConcreteState1()); context.Request(); context.Request(); context.Request(); context.Request();
经典用例——游戏中敌人ai 1 2 3 4 5 6 7 8 9 10 11 12 13 using System.Collections;using System.Collections.Generic;using UnityEngine;public interface Istate { public void OnStateEnter () ; public void OnStateProcess () ; public void OnStateExit () ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 using System.Collections;using System.Collections.Generic;using UnityEngine;public class States { }public class AlienIdle : Istate { private StateManager stateManager; public float time; public AlienIdle (StateManager stateManager ) { this .stateManager = stateManager; } public void OnStateEnter () { } public void OnStateExit () { } public void OnStateProcess () { Vector3 tarPos = stateManager.GetPlayerPos(); if ((tarPos - stateManager.transform.position).magnitude < 8.0f ) { Debug.Log("切换到walk状态" ); stateManager.ChangeState(stateType.walk); } } }public class AlienWalk : Istate { private StateManager stateManager; private float timer; private int direction; public AlienWalk (StateManager stateMachine ) { this .stateManager = stateMachine; } public void OnStateEnter () { stateManager._aiAnim.SetBool("walking" ,true ); if (stateManager.GetPlayerPos().x > stateManager.transform.position.x) { direction = 1 ; } else { direction = -1 ; } Debug.Log("进入了Walk状态" ); timer = 1.0f ; } public void OnStateExit () { stateManager._aiAnim.SetBool("walking" , false ); } public void OnStateProcess () { stateManager._aiRigid.velocity = new Vector2(direction*stateManager.speed,0 ); timer -= Time.deltaTime; if (timer < 0 ) { stateManager.ChangeState(stateType.idle); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 using System.Collections;using System.Collections.Generic;using UnityEngine;public enum stateType { idle, walk }public class StateManager : MonoBehaviour { public Animator _aiAnim; public Rigidbody2D _aiRigid; public Collider2D _aiCollider; public float speed = 2.0f ; public Istate _currentState; public stateType _state; public Dictionary<stateType, Istate> _stateMapping = new Dictionary<stateType, Istate>(); private void Awake () { _stateMapping.Add(stateType.idle, new AlienIdle(this )); _stateMapping.Add(stateType.walk, new AlienWalk(this )); _aiAnim = GetComponentInChildren<Animator>(); _aiRigid = GetComponent<Rigidbody2D>(); _aiCollider = GetComponent<Collider2D>(); _currentState = _stateMapping[stateType.idle]; } private void Update () { _currentState.OnStateProcess(); Filp(); } public void ChangeState (stateType state ) { if (_currentState != null ) { _currentState.OnStateExit(); } _currentState = _stateMapping[state]; _currentState.OnStateEnter(); } #region 公用方法 public Vector3 GetPlayerPos () { return PlayerController.Instance().transform.position; } public void Filp () { if (_aiRigid.velocity.x > .1 f) { transform.localScale = new Vector3(-1 ,1 ,1 ); }else if (_aiRigid.velocity.x < -.1 f) { transform.localScale = new Vector3(1 , 1 , 1 ); } } #endregion }
状态模式的好处
将特定状态相关的行为局部化,并将不同状态的行为分割开来。
将特定的状态相关的行为都放入一个对象中,由于所有与状态相关的代码都存在于ConcreteState中,所有通过定义新的子类可以很方便地增加新的状态和转换。
消除了庞大的条件分支语句
状态模式将各种状态转换的逻辑分布到State的子类之间,来减少相互间的依赖。
什么时候考虑用状态模式? 当一个对象的行为取决于他的状态,并且它必须在运行时刻根据状态改变它的行为时,就可以考虑使用状态模式。
枚举与状态模式+字典,天生一对。
1 2 3 4 5 6 enum StateType { idle, patrol, atk, }