设计一系列国家

时间:2010-04-26 09:51:33

标签: c# java architecture oop

我想建模一种FSM(有限状态机)。我有一系列状态(比方说,从StateA到StateZ)。该序列称为Chain,在内部实现为List。我将按照我希望它们运行的​​顺序添加状态。

我的目的是能够在我的计算机中执行一系列操作(例如,鼠标单击)。 (我知道这已经完成了数十万次)。

所以状态定义为:

  1. boolean Precondition()< - 检查是否为此状态,某些条件为真。例如,如果我想单击程序的“记录”按钮,在此方法中,我将检查程序的进程是否正在运行。如果是,则转到链表中的下一个状态,否则,转到定义为失败状态(通常是它们的第一个状态)。
  2. IState GetNextState()< - 返回要评估的下一个州。如果Precondition()成功,它应该产生链中的下一个状态,否则它应该产生失败状态。
  3. Run()只需检查Precondition()并设置内部数据,以便GetNextState()按预期工作。
  4. 所以,对此的一种天真的方法是这样的:

    Chain chain = new Chain();
    //chain.AddState(new State(Precondition, FailState, NextState) <- Method structure
    chain.AddState(new State(new WinampIsOpenCondition(), null, new <problem here, I want to referr to a state that still wasn't defined!>);
    

    最大的问题是我想引用一个目前尚未定义的国家。我可以通过在重写状态和使用内部哈希表时使用字符串来解决问题,但是没有更明确的替代方案吗?

    我只能在构造函数中传递前置条件和失败状态,让执行前的链在每个状态中放入公共属性中正确的下一个状态,但这看起来有点尴尬。

3 个答案:

答案 0 :(得分:2)

您可以执行以下操作之一:

  • nextState定义为Sta t e类中的可变字段,然后使用mutator将状态连接起来;例如setNextState。可以实现setNextState方法只允许调用一次;后续调用会导致IllegalStateException被抛出。
  • 更改State界面只是返回是否满足前提条件(即返回boolean)并使用外部“协调员”类在列表中进行转换条件得到满足。换句话说,你知道下一个状态是在索引i + 1,所以没有真正需要每个状态明确知道其后继者

鉴于你的状态机的简单性,我赞成第二种方法。

答案 1 :(得分:1)

这可以成为装饰者模式的完美候选者。下一步装饰(包装)当前步骤。你可以建立整个国家链。

答案 2 :(得分:1)

我同意@Adamski的第二点。除非您计划将状态作为图算法遍历而不是使用外部中介来管理遍历,否则状态不需要在位置上知道自己。

如果你真的对能够表达为树的状态感兴趣(即使它当前是完全线性的)来回答new <problem here, I want to referr to a state that still wasn't defined!>);上的问题

我解决这个问题的方法是对于我记录的每个动作,我会设置任何类型的容器,如数组,以保存动作列表。然后我会记录当前的操作,但是会延迟将其添加到容器中。当我记录第二个动作时,我会将其添加到上一个动作,然后将上一个动作推到数组上,然后保持当前动作。

当您进入执行调用时,您将推送不在容器上的最终操作以及定义FSM结束的操作。

所以你会有这样的东西

public State PreviousAction { get; set; }
public IList<State> States { get; private set }
public void QueueAction(State CurrentAction)
{    
    if(PreviousAction != null)
    {        
        States.Add(new State(PreviousAction, CurrentAction)        
    }

    PreviousAction = CurrentAction;    
}

public void Execute()
{    
    States.Add(new State(PreviousAction, State.Terminator));

    States[0].Execute();    
}