【问题标题】:Can you create a Finite State Machine using SOLID principles你能用 SOLID 原则创建一个有限状态机吗
【发布时间】:2022-10-23 17:58:24
【问题描述】:

我目前正在研究游戏开发,对于我们的一个项目,我们必须创建一个可以用作简单 AI 的有限状态机。一直鼓励使用 SOLID 原则编写代码,但对于这个项目,我想不出办法。虽然这个项目不需要它,但我仍然想找到一种方法来做到这一点。它是用 Unity c# 制作的。

public interface IState
{
    public IState CheckForState();
}

public class Walk : IState
{
    float speed = 10;

    public IState CheckForState()
    {
        if (speed > 5)
        {
            return Run;
        }
        return this;
    }
}

要使其稳定,您必须找到一种方法从函数中删除“运行”,因为如果删除了“运行”脚本,该函数将不再工作,但如果可用,它仍然应该能够获得它。

【问题讨论】:

  • 也许您可以详细说明为什么您认为此实现与 SOLID 冲突? Run 到底是什么,它在哪里定义?
  • 首先想到的是,一个了解运行状态的高级控制器应该告诉步行状态如果速度更慢或更高,应该改变什么......虽然不确定你是如何使用它的。
  • @MakePeaceGreatAgain 我相信它会破坏 SOLID,因为如果要删除 Run 脚本,Walk 脚本会中断,Walk 脚本依赖于它的存在,因此会破坏依赖倒置原则,就我而言我知道。 Run 是 AI 可能处于的状态。

标签: c# unity3d interface state-machine


【解决方案1】:

想到了几个解决方案,它们可以实现完全 SOLID 的解决方案。

解决方案 1

满足依赖倒置原则的第一个也是最简单的方法是将Run 类注入Walk 类,如下所示:

public class Walk : IState
{
    float speed = 10;
    private readonly IState _runState;
    public Walk(IState runState)
    {
        _runState = runState;
    }

    public IState CheckForState()
    {
        if (speed > 5)
        {
            return _runState;
        }
        return this;
    }
}

这个解决方案使得 Walk 不再依赖于 Run 的具体实现,但仍然允许它转换到运行状态。但是,这确实需要Walk 知道它可以转换到哪些状态(我认为这是一个合理的预期)。

解决方案 2

您可以通过将状态转换表示为对象来进一步解耦。这看起来像这样:

public interface IState
{
    public IState CheckForState();
    public float Value {get;}
}

public interface IStateTransition
{
    public IState CheckTransition(IState source);
}

public class GreaterThanTransition : IStateTransition
{
    private readonly IState _targetState;
    private readonly int _checkValue;

    public GreaterThanTransition(IState targetState, int checkValue)
    {
        _targetState = targetState;
        _checkValue = checkValue;
    }
    public IState CheckTransition(IState sourceState) => sourceState.Value > _checkValue ? _targetState : sourceState;
}

public class Walk : IState
{
    private readonly List<IStateTransition> _transitions;

    public Walk(List<IStateTransition> transitions)
    {
        _transitions = transitions;
    }
    public float Value => 10;

    public IState CheckForState() => _transitions.Select(t => t.CheckTransition(this)).FirstOrDefault(s => s != this) ?? this;
}

然后你可以像这样创建一个 Walk 实例:

new Walk(new List<IStateTransition>
{
    new GreaterThanTransition(new Run(...), 5)
});

这要求可以在IStateTransition 中使用来自状态对象的值来检查是否需要发生状态转换。我在示例中使用了int 作为Value 属性的类型,但您可以轻松地将其切换为泛型类型。

该解决方案将每个状态完全解耦,并将状态转换捕获为可重用组件。它在某些方面更加复杂,因为代码现在被拆分为更多的类和接口,但它也完全结合了 SOLID 原则。

【讨论】:

    猜你喜欢
    • 2017-01-04
    • 1970-01-01
    • 1970-01-01
    • 2022-10-14
    • 1970-01-01
    • 2023-03-13
    • 2014-01-11
    • 2022-01-16
    • 1970-01-01
    相关资源
    最近更新 更多