设计模式之游戏--状态模式

状态模式是一种设计模式,用于解决对象在不同状态下的行为管理。通过将状态相关的行为封装到各自的状态类中,可以简化复杂的逻辑判断,提高代码的可读性和可扩展性。在示例中,通过StateManager管理人物的IdleState、ChaseState和AttackState,根据条件触发状态转换,实现了游戏人物在不同状态下的行为切换,遵循了单一职责原则,减少了对象间的依赖,同时也便于添加新的状态和转换。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.引入问题

在软件开发过程中,应用程序可能会根据不同的情况作出不同的处理。最直接的解决方案是将这些所有可能发生的情况全都考虑到。然后使用判断语句来做状态判断来进行不同情况的处理。但是对复杂状态的判断就显得“力不从心了”。随着增加新的状态或者修改一个状态导致判断语句的增多或者修改可能会引起很大的修改,而且程序的可读性,扩展性也会变得很弱。维护也会很麻烦。此时我们的状态模式就孕育而生了。

而状态模式的作用就是:解决当控制一个对象状态转换的条件表达式过于复杂时,把状态的判断逻辑转移到表示不同的一系列类当中,把复杂的逻辑判断简单化,其意图是让一个对象在其内部状态改变的时候其行为也随之改变,是一种对象行为型模式

定义

当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。这个有点晦涩的定义后面总结的时候再细讲。

UML图

在这里插入图片描述
StateManager:状态管理者,它的作用是维护一个当前状态,并将与状态相关的操作委托给当前状态对象来处理。
State:抽象状态,用于封装状态的行为和属性,简单来说就是提取各状态的共同行为和属性。
StateA:具体状态,实现抽象状态的具体行为。

具体示例:

下面我们以游戏中人物的人物为例,实现人物的状态机。
StateManager(状态管理者):

using System;
using System.Collections.Generic;
using UnityEngine;

//人物行为枚举
public enum Actions
{
    NullAction = 0,
    SeeEnemy,//看到敌人
    NoEnemy,//失去敌人
    CanAttack//攻击敌人

}
//人物状态ID
public enum StateID
{
    NullState,
    Idle,//站立
    Chase,//追赶(奔跑)
    Attack,//攻击
}

public class StateManager
{
    private List<State> states = new List<State>();
    private State mCurrent;

    public State CurrentState
    {
        get
        {
            return mCurrent;
        }
       
    }

    public void AddState(State state)
    {
        if (state==null)
        {
            Debug.LogError("state 不能为空null");
            return;
        }
        else
        {
            if (states.Count==0)
            {
                states.Add(state);
                mCurrent = state;
                mCurrent.Enter();
                return;
            }
            foreach (State item in states)
            {
                if (item == state)
                {
                    Debug.LogError("states 已经存在state");
                    return;
                }
            }
            states.Add(state);
        }   
    }
    public void AddState(params State[] states)
    {
        foreach (State item in states)
        {
            AddState(item);
        }
    }
    public void DeleteState(StateID stateID)
    {
        if (states.Count==0)
        {
            Debug.LogError("states为空");
        }
        foreach (State item in states)
        {
            if (item.StateID== stateID)
            {
                states.Remove(item);
                return;
            }
        }
        Debug.LogError("states 不存在" + stateID.ToString());
    }

    public void ChangeState(Actions action)
    {
        if (action== Actions.NullAction)
        {
            Debug.LogError("action 不能为空");
        }
        StateID nextstate = mCurrent.GetNextState(action);
        if (nextstate == StateID.NullState)
        {
            Debug.LogError("nextstate为空");
            return ;
        }
        foreach (State item in states)
        {
            if (item.StateID== nextstate)
            {
                mCurrent.Leave();
                mCurrent = item;
                item.Enter();
                return;
            }
        }
        Debug.LogError(mCurrent+"没有"+ action);

    }

    
    public void print(string riZhi)
    {
        Debug.Log(riZhi);
    }

}

人物抽象状态:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;

  public abstract class State
{
    private Dictionary<Actions, StateID> actionDic = new Dictionary<Actions, StateID>();
    private StateID mstateID;
    protected StateManager stateFSM;
    public State(StateManager stateManager,StateID currentState)
    {
        stateFSM = stateManager;
        mstateID = currentState;
    }
    public StateID StateID
    {
        get
        {
            return mstateID;
        }
    }
    public StateID GetNextState(Actions action)
    {
        if (action==Actions.NullAction)
        {
            Debug.LogError("action不能为空");
            return StateID.NullState;
        }
        if (actionDic.ContainsKey(action)==false)
        {
            Debug.LogError(mstateID+"不包含:" + action);
            return StateID.NullState;
        }

        return actionDic[action];
    }

    public abstract void Enter();
    public abstract void Leave();


    public abstract void UpdateState();

    public void AddState(Actions action,StateID stateID)
    {
        if (action == Actions.NullAction)
        {
            Debug.LogError("action不能为空");
            return ;
        }
        if (actionDic.ContainsKey(action) == true)
        {
            Debug.LogError("actionDic已包含:" + action);
            return ;
        }
        actionDic.Add(action, stateID);
    }

    public void DeleteState(Actions action)
    {
        if (action == Actions.NullAction)
        {
            Debug.LogError("action不能为空");
            return;
        }
        if (actionDic.ContainsKey(action) == false)
        {
            Debug.LogError("actionDic没有包含:" + action);
            return;
        }
        actionDic.Remove(action);
    }
}

人物站立状态:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

public class IdleState:State
{
    public IdleState(StateManager stateManager, StateID currentState) : base(stateManager, currentState)
    {
     
    }

    public override void Enter()
    {
        stateFSM.print("进入IdleState");
    }

    public override void Leave()
    {
        stateFSM.print("离开IdleState");
    }

    public override void UpdateState()
    {
        
        if (StartClient.command=="触发从Idle状态转换为Chase状态的条件")
        {
            stateFSM.print("触发从Idle状态转换为Chase状态的条件");
            stateFSM.ChangeState(Actions.SeeEnemy);
        }
    }
}

人物追赶状态:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


 public   class ChaseState:State
    {
    public ChaseState(StateManager stateManager, StateID currentState) : base(stateManager, currentState)
    {

    }

    public override void Enter()
    {
        stateFSM.print("进入ChaseState");
    }

    public override void Leave()
    {
        stateFSM.print("离开ChaseState");
    }

    public override void UpdateState()
    {
        if (StartClient.command == "触发从Chase状态转换为Attack状态的条件")
        {
            stateFSM.print("触发从Chase状态转换为Attack状态的条件");
            stateFSM.ChangeState(Actions.CanAttack);
        }
        if (StartClient.command == "触发从Chase状态转换为Idle状态的条件")
        {
            stateFSM.print("触发从Chase状态转换为Idle状态的条件");
            stateFSM.ChangeState(Actions.NoEnemy);
        }
    }

}


人物攻击状态:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;



   public class AttackState:State
   {
     public AttackState(StateManager stateManager, StateID currentState):base(stateManager,currentState)
    {
  
    }

    public override void Enter()
    {
        stateFSM.print("进入AttackState");
    }

    public override void Leave()
    {
        stateFSM.print("离开AttackState");
    }

    public override void UpdateState()
    {
        if (StartClient.command == "触发从Attack状态转换为Chase状态的条件")
        {
            stateFSM.print("触发从Attack状态转换为Chase状态的条件");
            stateFSM.ChangeState(Actions.SeeEnemy);
        }

        if (StartClient.command == "触发从Attack状态转换为Idle状态的条件")
        {
            stateFSM.print("触发从Attack状态转换为Idle状态的条件");
            stateFSM.ChangeState(Actions.NoEnemy);
        }
    }
}

客户端示例:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;

public  class StartClient:MonoBehaviour
{
    public static string command;
    public StateManager playerStateFSM;
    void Start()
    {
         playerStateFSM = new StateManager();

        IdleState idlestate = new IdleState(playerStateFSM, StateID.Idle);
        idlestate.AddState(Actions.SeeEnemy, StateID.Chase);

        ChaseState chaseState = new ChaseState(playerStateFSM, StateID.Chase);
        chaseState.AddState(Actions.NoEnemy, StateID.Idle);
        chaseState.AddState(Actions.CanAttack, StateID.Attack);

        AttackState attackState = new AttackState(playerStateFSM, StateID.Attack);
        attackState.AddState(Actions.NoEnemy, StateID.Idle);
        attackState.AddState(Actions.SeeEnemy, StateID.Chase);

        playerStateFSM.AddState(idlestate, chaseState, attackState);


     
        Invoke("SendCommand1", 1);
        Invoke("SendCommand2", 2);
        Invoke("SendCommand3",3);
        Invoke("SendCommand4",4);
    }
  
        

    public void SendCommand1()
    {
      
        command = "触发从Idle状态转换为Chase状态的条件";

      
    }
    public void SendCommand2()
    {
       
        command = "触发从Chase状态转换为Attack状态的条件";

       
    }
    public void SendCommand3()
    {
      

        command = "触发从Attack状态转换为Chase状态的条件";

     
    }
    public void SendCommand4()
    {
     

        command = "触发从Chase状态转换为Idle状态的条件";
    }

    private void Update()
    {
        playerStateFSM.CurrentState.UpdateState();
    }

}

请添加图片描述

总结:

优点:
1.状态模式将与特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,满足“单一职责原则”。就像上面的这个例子一样,人物有很多不同的行为–站立,追赶,攻击…,并且这些行为内部是复杂的,而状态模式把这些行为对应到特定的相关状态,也就是我们上面代码所写的IdleState,ChaseState,AttackState等具体状态,把这些行为的复杂逻辑放到对应的状态进行处理。当当前的状态满足切换条件时也就是UpdateState的判断逻辑成立,我们通过状态管理者StateManager改变当前状态,而在进入新状态时就可以执行相关的行为切换逻辑,通常在Enter()方法里面实现。这也是定义:“当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类”的具体表现。

2.它减少对象间的相互依赖。将不同的状态提取成独立的对象中会使得状态转换变得更加明确,且减少对象间的相互依赖。观察上面的代码,我们可以很容易的发现IdleState,ChaseState,AttackState这些底层状态并不存在依赖关系,它们都依赖于抽象State,而我们的高层状态管理逻辑StateManager并不依赖于具体的状态,它也依赖与我们的抽象State,这样我们修改StateManager的几率就大大降低了,这其实就是我们的依赖倒置原则,具体的依赖倒置原则的内容可以看我写的这篇文章。

3.有利于程序的扩展。通过定义新的子类很容易地增加新的状态和转换,也就是我们的开闭原则。

缺点:
1.状态模式的使用必然会增加系统的类与对象的个数。
2.状态模式的结构与实现都较为复杂,如果使用不当会导致程序结构和代码的混乱。

如果本文有什么错误还望指出,如果您觉得写的不错,欢迎三连支持。

<think>好的,我现在需要回答用户关于Java中状态模式的设计与实现的问题,并且根据提供的引用内容来支持回答。首先,我要确保理解状态模式的基本概念和应用场景。 用户提供的引用中,状态模式被定义为一种行为型设计模式,允许对象在内部状态改变时改变其行为,而无需修改其代码。引用[2]提到状态模式通过将行为封装到不同的状态类中,使对象在不同状态下有不同的行为。引用[4]也指出,状态模式在对象内部状态改变时改变其行为,并将行为封装在不同的状态对象中。这些信息帮助我确认状态模式的核心思想是状态与行为的分离。 接下来,用户需要具体的Java示例。引用[3]提到了订单状态的处理,这可能是一个典型的应用场景,比如电商系统中的订单状态机。结合引用[1]中的电子商务项目实例,我可以设计一个订单状态的例子,展示不同状态下的行为变化。 我需要构建一个结构清晰的示例,包括上下文类(Context)和具体的状态类。根据引用中的信息,状态模式的结构通常包含一个抽象状态接口或抽象类,具体状态类实现该接口,上下文类持有当前状态并委托行为给状态对象。 此外,要确保代码符合Java规范,使用合适的设计,比如将状态转换逻辑放在具体状态类中,或者由上下文类管理状态转换。根据引用[3]中的状态机业务实现,可能状态转换由状态类自身触发,比如处理完一个操作后切换到下一个状态。 在编写示例时,需要注意状态之间的转换条件和行为。例如,订单从“待支付”变为“已支付”,再变为“已发货”等。每个状态处理的操作(如支付、发货)会导致状态变更,这需要在具体状态类中实现。 最后,根据用户提供的引用内容,我需要正确标注引用来源。例如,在解释状态模式定义时引用[2]和[4],在提到电商应用时引用[1]和[3]。 现在,我需要将这些思路整合成一个结构清晰的回答,包含定义、结构、实现步骤、示例代码和应用场景,并在适当位置添加引用标注。同时,生成相关问题以帮助用户进一步理解。</think>### Java 状态模式的设计与实现 #### 1. 定义与核心思想 状态模式(State Pattern)是一种行为型设计模式,允许对象在内部状态改变时改变其行为。其核心是将**对象的行为封装到不同的状态类**中,通过切换状态对象实现行为的动态调整,避免大量条件判断语句[^2][^4]。 #### 2. 模式结构 - **上下文类(Context)**:持有当前状态对象,定义客户端调用的接口。 - **抽象状态类(State)**:定义状态行为的接口。 - **具体状态类(ConcreteState)**:实现抽象状态类的行为逻辑,可能触发状态转换。 #### 3. 实现步骤(以电商订单状态为例) ##### 步骤1:定义状态接口 ```java public interface OrderState { void pay(OrderContext context); // 支付操作 void ship(OrderContext context); // 发货操作 } ``` ##### 步骤2:实现具体状态类 - **待支付状态** ```java public class UnpaidState implements OrderState { @Override public void pay(OrderContext context) { System.out.println("支付成功"); context.setState(new PaidState()); // 状态切换 } @Override public void ship(OrderContext context) { System.out.println("错误:尚未支付,无法发货"); } } ``` - **已支付状态** ```java public class PaidState implements OrderState { @Override public void pay(OrderContext context) { System.out.println("错误:订单已支付"); } @Override public void ship(OrderContext context) { System.out.println("发货成功"); context.setState(new ShippedState()); // 状态切换 } } ``` ##### 步骤3:定义上下文类 ```java public class OrderContext { private OrderState currentState; public OrderContext() { this.currentState = new UnpaidState(); // 初始状态 } public void setState(OrderState state) { this.currentState = state; } public void pay() { currentState.pay(this); // 委托给当前状态 } public void ship() { currentState.ship(this); } } ``` #### 4. 使用示例 ```java public class Client { public static void main(String[] args) { OrderContext order = new OrderContext(); order.ship(); // 输出:错误:尚未支付,无法发货 order.pay(); // 输出:支付成功 order.ship(); // 输出:发货成功 } } ``` #### 5. 应用场景 - **订单状态流转**:如电商系统中订单的“待支付→已支付→已发货”流程[^3]。 - **游戏角色状态**:角色“站立→移动→攻击”状态切换。 - **硬件设备控制**:打印机“空闲→打印→缺纸”状态管理。 #### 6. 优势与注意事项 - **优势**:消除复杂条件分支、符合开闭原则、状态转换逻辑集中管理[^1][^2]。 - **注意事项**:避免状态类膨胀(需合理拆分粒度)、上下文类与状态类的职责边界需清晰。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值