【问题标题】:What is the best practice for a hierarchical state machine using the state pattern?使用状态模式的分层状态机的最佳实践是什么?
【发布时间】:2011-03-30 01:38:58
【问题描述】:

我即将使用状态模式在 C# 中实现分层状态机。作为指南,我使用this 示例。但是,该示例并未提供有关分层状态的答案。不幸的是,我似乎无法在其他地方找到好的例子。我的第一个想法是为分层状态创建嵌套类。但这被认为是最佳做法还是有更好的解决方案?

你好!

更新:

我整个下午都在尝试实现上述状态模式。 HSM 基于一个非常简单的媒体播放器:

alt text http://www.freeimagehosting.net/uploads/e8d2d6486a.jpg

我以为我已经做到了,但有一件事我不明白。首先是我写的代码(对不起,很多):

public class MediaPlayer
{
    public MediaPlayerStates state;

    public MediaPlayer(MediaPlayerStates state)
    {
        this.state = state;
    }

    public void OnButtonPressed()
    {
        state.OnButtonPressed(this);
    }

    public void DeviceBooted()
    { 
        state. ?????
    }

    //Other Functions
}

//The 3 initial states (Start, On, End) know only 2 events.
public abstract class MediaPlayerStates
{
    public abstract void OnButtonPressed(MediaPlayer player);
    public abstract void OffButtonPressed(MediaPlayer player);
}

//The very beginpoint of the state machine
public class Start : MediaPlayerStates
{
    //When hitting the onbutton, the state changes to the OnState state
    public override void OnButtonPressed(MediaPlayer player)
    {
        player.state = new OnState(player);
    }

    //No need to implement this one
    public override void OffButtonPressed(MediaPlayer player)
    {
        throw new NotImplementedException();
    }
}

//OnState implements the 2 events from the MediaPlayerStates abstract class.
public class OnState : MediaPlayerStates
{
    //When entered the OnState state, a new entrypoint is creaeted: the Start state
    public OnState(MediaPlayer player)
    {
        player.state = new OnStartState();
    }

    //The OnState doesn't have a OnButtonPressed event so it doesn't need to be implemented
    public override void OnButtonPressed(MediaPlayer player)
    {
        throw new NotImplementedException();
    }

    //When hitting the offbutton in the OnState, the new state is End
    public override void OffButtonPressed(MediaPlayer player)
    {
        player.state = new End();
    }

    //The OnState itself containts 3 events, therefore these need to be implemented by every state whitin the OnState state
    public abstract class SubStates : MediaPlayerStates
    {
        public abstract void DeviceBooted(MediaPlayer player);
        public abstract void PlayButtonPressed(MediaPlayer player);
        public abstract void StopButtonPressed(MediaPlayer player);
    }

    //The OnStartState is the pseudoState where the On state starts
    public class OnStartState : SubStates
    {
        //When booted, the state of the player changes to the ShowMediaFileState state
        public override void DeviceBooted(MediaPlayer player)
        {
            player.state = new ShowMediaFileState();
        }

        //The events below don't need to be implemented since they don't exist. 
        public override void PlayButtonPressed(MediaPlayer player)
        {
            throw new NotImplementedException();
        }

        public override void StopButtonPressed(MediaPlayer player)
        {
            throw new NotImplementedException();
        }

        public override void OnButtonPressed(MediaPlayer player)
        {
            throw new NotImplementedException();
        }

        public override void OffButtonPressed(MediaPlayer player)
        {
            throw new NotImplementedException();
        }
    }

    public class ShowMediaFileState : SubStates
    {
        //This event doesn't exists for this state
        public override void DeviceBooted(MediaPlayer player)
        {
            throw new NotImplementedException();
        }

        //When hitting the play button in this state, play the mediafile
        public override void PlayButtonPressed(MediaPlayer player)
        {
            player.state = new PlayMediaFileState();
        }

        //These events also don't exist for this state
        public override void StopButtonPressed(MediaPlayer player)
        {
            throw new NotImplementedException();
        }

        public override void OnButtonPressed(MediaPlayer player)
        {
            throw new NotImplementedException();
        }

        public override void OffButtonPressed(MediaPlayer player)
        {
            throw new NotImplementedException();
        }
    }

    public class PlayMediaFileState : SubStates
    {
        //This event doesn't exist for this state
        public override void DeviceBooted(MediaPlayer player)
        {
            throw new NotImplementedException();
        }

        //This event doesn't exist for this state
        public override void PlayButtonPressed(MediaPlayer player)
        {
            throw new NotImplementedException();
        }

        //While playing a file and hitting the stopbutton, the state changes to the ShowMediaFileState state
        public override void StopButtonPressed(MediaPlayer player)
        {
            player.state = new ShowMediaFileState();
        }

        //This event doesn't exist for this state
        public override void OnButtonPressed(MediaPlayer player)
        {
            throw new NotImplementedException();
        }

        //This event doesn't exist for this state
        public override void OffButtonPressed(MediaPlayer player)
        {
            throw new NotImplementedException();
        }
    }
}

//The endstate doesn't need any implementation since there cannot occur a event while being off
public class End : MediaPlayerStates
{
    public override void OnButtonPressed(MediaPlayer player)
    {
        throw new NotImplementedException();
    }

    public override void OffButtonPressed(MediaPlayer player)
    {
        throw new NotImplementedException();
    }
}

在 MediaPlayer 类中定义事件时,我无法调用任何其他函数

  • OnButtonPressed
  • OffButtonPressed

所以我想知道,我的实现有什么好处吗?怎么了?我还尝试查看使用复合模式的建议,但我不明白它应该如何与状态模式一起使用。希望任何人都可以提供帮助!

【问题讨论】:

标签: c# class nested hierarchical fsm


【解决方案1】:

要使其以通用方式工作,您需要将状态机的层次结构视为树结构;节点之间的转换可以对树使用最小共同祖先 (LCA) 算法,然后从源节点祖先上的共同祖先下面的节点退出(级联退出到任何子节点),然后从目标节点祖先上的每个节点进入目标节点的共同祖先之下的节点,最后,如果目标节点有子节点,则需要像进入任何其他复合状态一样输入这些子节点。

这是 UML 上层结构规范中提到的方法。

看看https://github.com/steelbreeze/state.cs中的源代码,因为它实现了上面的方法。

要查看一个工作示例,请在此处查看姐妹 JavaScript 版本的项目站点:http://www.steelbreeze.net/state.js/

【讨论】:

    【解决方案2】:

    要使用状态模式制作 HSM,每个具有子状态的状态本身都必须是状态机。这样上层就不知道子状态(副作用更少),并且状态可以更好地管理他的子状态(可以有一个默认状态,可以记住它所处的最后一个状态等)。

    顺便说一句,当您无法对某个操作执行任何有用的操作时抛出异常是错误的。你应该忽略它。您只在特殊情况下抛出异常,按错按钮是预期的用户行为。

    【讨论】:

    • +1: “...按错按钮是预期的用户行为。”这是很正常的。很少有例外情况——我希望程序员能理解这一点!
    • 不能同意概括“当用户按下错误按钮时你应该忽略......”。我真的很讨厌那种应用程序,当我有充分的理由按下明显好的按钮时,它什么也没告诉我。作为用户,我应该知道我做错了什么,而不必在互联网上找到解决方案......更不用说 NotImplementedException 是编写代码的正确方法 - 它显然还没有完成,作为程序员你不想忘记可能导致数据损坏的场景。
    • 我不是在概括,而是在谈论这个确切的例子。我的偏好是禁用不可用的按钮。在这种情况下,未实施的例外是一种滥用,因为它永远无法实施。
    【解决方案3】:

    在您开始实现您自己的 FSM 框架之前,请查看SMC - 状态机编译器。

    SMC 采用状态机的文本定义并生成代码来实现它。它具有包括 C# 在内的多种语言的后端。还可以输出点文件,生成FSM图。

    SMC 可以通过 push 和 pop 转换创建类似于分层状态机的东西 - 本质上是 push 将控制权转移到新的状态机,而 pop 将控制权返回给原始状态机。

    【讨论】:

    • 一个可以生成 FSM 的框架看起来有多棒,我宁愿尝试创建自己的 FSM。除此之外,我不想在我的项目中有很多框架文件。我只想创建一个简单干净的FSM。
    【解决方案4】:

    我想你也会想要 Composite;这将允许您将状态机链接在一起。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-09-09
      • 1970-01-01
      • 2014-02-20
      • 1970-01-01
      • 2019-01-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多