7.状态模式

7.行为型模式-状态模式


7.1 基础知识

学习难度:3

使用频率:4

总分:6

定义

状态模式(State Pattern)允许一个对象在其内在状态改变时改变其行为,使得对象看起来像是改变了其类。

说人话

状态模式就像是给对象穿上了不同的外衣,让它在不同的状态下表现出不同的行为。

结构图

实现步骤

  • 状态接口:定义状态通用方法,例如进入状态和离开状态的方法,以及可能的行为方法。通常会将上下文类作为参数传入,以便状态对象能够操作上下文。
  • 多个具体状态类:实现状态接口,定义具体状态下的行为逻辑。
  • 上下文类:维护当前状态对象,并在需要时调用状态对象的方法。定义切换状态的方法,允许外部传入新的状态对象并更新当前状态。
  • 客户端:实例化多个具体状态类和上下文类,并将具体状态对象赋值给上下文。通过上下文对象来控制状态的切换和行为的执行。

说明

状态模式可以让代码更加清晰,避免了大量的条件语句,提高了可维护性和扩展性。通过状态模式,可以很容易地新增、删除或修改状态,而不需要修改上下文类中的逻辑。


7.2 模版代码

状态接口和具体状态类

// 状态接口
public interface IState
{
    void Handle(Context context);
}

// 具体状态类A
public class ConcreteStateA : IState
{
    public void Handle(Context context)
    {
        Console.WriteLine("上下文处于状态A,执行相关逻辑");
        // 这里可以执行与状态A相关的操作
        // 也可以根据条件切换到不同的状态
        Console.WriteLine("切换至状态B");
        context.SetState(new ConcreteStateB());
    }
}

// 具体状态类B
public class ConcreteStateB : IState
{
    public void Handle(Context context)
    {
        Console.WriteLine("上下文处于状态B,执行相关逻辑");
        // 这里可以执行与状态B相关的操作
        // 也可以根据条件切换到不同的状态
        Console.WriteLine("切换至状态A");
        context.SetState(new ConcreteStateA());
    }
}

上下文类

// 上下文类
public class Context
{
    private IState currentState;

    public Context(IState state)
    {
        currentState = state;
    }

    public void SetState(IState state)
    {
        currentState = state;
    }

    public void Request()
    {
        currentState.Handle(this);
    }
}

客户端

class Program
{
    static void Main(string[] args)
    {
        //实例化上下文类 初始为状态A
        Context context = new Context(new ConcreteStateA());

        context.Request(); // 初始状态为ConcreteStateA,执行ConcreteStateA的操作
        // 上下文处于状态A,执行相关逻辑
        // 切换至状态B

        context.Request(); // 切换到ConcreteStateB,执行ConcreteStateB的操作
        // 上下文处于状态B,执行相关逻辑
        // 切换至状态A
        
        context.Request(); // 再次切换到ConcreteStateA,执行ConcreteStateA的操作
        // 上下文处于状态A,执行相关逻辑
        // 切换至状态B
    }
}

7.3 CSharp实践

实践需求

使用状态模式,模拟员工普通工作,认真工作,和摸鱼状态。

状态接口和具体员工状态类

// 状态接口
public interface IState
{
    void DoWork();
}

// 具体状态类:工作中
public class WorkingState : IState
{
    public void DoWork()
    {
        Console.WriteLine("员工正在进行普通工作...");
    }
}

// 具体状态类:摸鱼中
public class SlackingState : IState
{
    public void DoWork()
    {
        Console.WriteLine("员工正在摸鱼...");
    }
}

// 具体状态类:认真工作中
public class FocusedState : IState
{
    public void DoWork()
    {
        Console.WriteLine("员工正在专心工作...");
    }
}

员工类

// 上下文类:员工
public class Employee
{
    private IState currentState;

    public Employee()
    {
        // 初始状态为工作中
        currentState = new WorkingState();
    }

    public void SetState(IState state)
    {
        currentState = state;
    }

    public void DoWork()
    {
        currentState.DoWork();
    }
}

客户端

class Program
{
    static void Main(string[] args)
    {
        Employee employee = new Employee();

        employee.DoWork(); // 初始状态为工作中,执行工作中的操作
        employee.SetState(new SlackingState()); // 切换到摸鱼中状态
        employee.DoWork(); // 员工处于摸鱼中状态,执行摸鱼的操作
        employee.SetState(new FocusedState()); // 切换到认真工作中状态
        employee.DoWork(); // 员工处于认真工作中状态,执行专心工作的操作
    }
}

7.4 Unity实践

实践需求

使用状态模式,实现按空格随机切换薇恩为默认,移动和攻击状态。

薇恩状态接口和具体薇恩状态类

public interface IVayneState
{
    // 进入状态的方法
    void EnterState();

    // 退出状态的方法
    void ExitState();

    // 记录状态的方法
    void LogState();
}

public class VayneIdleState : IVayneState
{
    private Animator animator; 

    public VayneIdleState(Animator animator)
    {
        this.animator = animator;
    }

    public void EnterState()
    {
        animator.SetBool("IsIdle", true);
    }

    public void ExitState()
    {
        animator.SetBool("IsIdle", false);
    }

    public void LogState()
    {
        Debug.Log("薇恩正在默认状态");
    }
}

public class VayneRunState : IVayneState
{
    private Animator animator;

    public VayneRunState(Animator animator)
    {
        this.animator = animator;
    }

    public void EnterState()
    {
        animator.SetBool("IsRunning", true); // 播放跑步动画
    }

    public void ExitState()
    {
        animator.SetBool("IsRunning", false); // 停止跑步动画
    }

    public void LogState()
    {
        // 在跑步状态下执行的行为
        Debug.Log("薇恩正在移动");
    }
}

public class VayneAttackState : IVayneState
{
    private Animator animator;

    public VayneAttackState(Animator animator)
    {
        this.animator = animator;
    }

    public void EnterState()
    {
        animator.SetTrigger("Attack"); // 播放攻击动画
    }

    public void ExitState()
    {
        // 退出攻击状态的逻辑
    }

    public void LogState()
    {
        // 在攻击状态下执行的行为
        Debug.Log("薇恩正在攻击...");
    }
}

薇恩上下文类

public class VayneContext
{
    private IVayneState currentState;

    public VayneContext(IVayneState state)
    {
        currentState = state;
        // 进入新状态
        currentState.EnterState(); // 调用当前状态的进入状态方法
    }

    public void ChangeState(IVayneState newState)
    {
        if (newState == currentState) return;
        // 退出当前状态
        currentState.ExitState(); // 调用当前状态的退出状态方法

        // 切换到新状态
        currentState = newState;

        // 进入新状态
        currentState.EnterState(); // 调用新状态的进入状态方法
    }

    public void LogState()
    {
        currentState.LogState(); // 打印当前状态信息
    }
}

客户端

public class TestStatePattern : MonoBehaviour
{
    private List<IVayneState> vayneStateList = new List<IVayneState>(); // 创建 IVayneState 接口的列表

    private VayneContext vayneContext; // 创建 VayneContext 对象

    private void Start()
    {
        GameObject vayneGameObject = Instantiate(Resources.Load<GameObject>("Lesson25_行为型模式_状态模式/Vayne")); // 实例化 Vayne 游戏对象

        Animator vayneAnimator = vayneGameObject.GetComponent<Animator>(); // 获取 Vayne 游戏对象上的 Animator 组件

        IVayneState idleState = new VayneIdleState(vayneAnimator); // 创建 VayneIdleState 状态对象
        IVayneState runState = new VayneRunState(vayneAnimator); // 创建 VayneRunState 状态对象
        IVayneState attackState = new VayneAttackState(vayneAnimator); // 创建 VayneAttackState 状态对象

        vayneStateList.Add(idleState); // 将 idleState 添加到状态列表
        vayneStateList.Add(runState); // 将 runState 添加到状态列表
        vayneStateList.Add(attackState); // 将 attackState 添加到状态列表

        vayneContext = new VayneContext(idleState); // 创建 VayneContext 上下文对象,初始状态为 idleState
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            vayneContext.ChangeState(vayneStateList[Random.Range(0, vayneStateList.Count)]); // 在状态列表中随机切换状态
            vayneContext.LogState(); // 记录当前状态
        }
    }
}

运行结果






转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏