我想建模一种FSM(有限状态机)。我有一系列状态(比方说,从StateA到StateZ)。该序列称为Chain,在内部实现为List。我将按照我希望它们运行的顺序添加状态。
我的目的是能够在我的计算机中执行一系列操作(例如,鼠标单击)。 (我知道这已经完成了数十万次)。
所以状态定义为:
boolean Precondition()
< - 检查是否为此状态,某些条件为真。例如,如果我想单击程序的“记录”按钮,在此方法中,我将检查程序的进程是否正在运行。如果是,则转到链表中的下一个状态,否则,转到定义为失败状态(通常是它们的第一个状态)。IState GetNextState()
< - 返回要评估的下一个州。如果Precondition()成功,它应该产生链中的下一个状态,否则它应该产生失败状态。Run()
只需检查Precondition()
并设置内部数据,以便GetNextState()
按预期工作。所以,对此的一种天真的方法是这样的:
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!>);
最大的问题是我想引用一个目前尚未定义的国家。我可以通过在重写状态和使用内部哈希表时使用字符串来解决问题,但是没有更明确的替代方案吗?
我只能在构造函数中传递前置条件和失败状态,让执行前的链在每个状态中放入公共属性中正确的下一个状态,但这看起来有点尴尬。
答案 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();
}