views:

74

answers:

3

I want to model a kind of FSM(Finite State Machine). I have a sequence of states (let's say, from StateA to StateZ). This sequence is called a Chain and is implemented internally as a List. I will add states by the order I want them to run.

My purpose is to be able to make a sequence of actions in my computer (for example, mouse clicks). (I know this has been done a zillion times).

So a state is defined as a:

  1. boolean Precondition() <- Checks to see if for this state, some condition is true. For example, if I want to click in the Record button of a program, in this method I would check if the program's process is running or not. If it is, go to the next state in the chain list, otherwise, go to what was defined as the fail state (generally is the first state of them all).
  2. IState GetNextState() <- Returns the next state to evaluate. If Precondition() was sucessful, it should yield the next state in the chain otherwise it should yield the fail state.
  3. Run() Simply checks the Precondition() and sets the internal data so GetNextState() works as expected.

So, a naive approach to this would be something like this:

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!>);

The big problem is that I want to make a reference to a State that at this point still wasn't defined. I could circumvent the problem by using strings when refrering to states and using an internal hashtable, but isn't there a clearer alternative?

I could just pass only the pre-condition and failure states in the constructor, having the chain just before execution put in each state the correct next state in a public property but that seems kind of awkward.

+2  A: 

You could do one of the following:

  • Define nextState as a mutable field within your State class, and wire up the states afterwards using a mutator; e.g. setNextState. The setNextState method could be implemented to only allow it to be called once; subsequent calls would cause an IllegalStateException to be thrown.
  • Change the State interface to simply return whether a pre-condition is met or not (i.e. return a boolean) and use an external "coordinator" class to transition along the list if the pre-condition is met. In other words, you know that the next state is at index i+1 so there's no real need for each state to have explicit knowledge of its successor.

Given the simplicity of your state machine I would favour the second approach.

Adamski
A: 

This can be a perfect candidate for Decorator pattern. Next step decorates(wraps) the current step. You can build the whole chain of states.

this. __curious_geek
+1  A: 

I agree strongly with the second point from @Adamski. There is no need for the states to be positionally aware of themselves unless you plan to traverse states as a graph algorithm as opposed to using an outside mediator to manage traversal.

If you really are interested in the states to actually be able to be expressed as a tree (even if it's entirely linear currently) to answer your question on new <problem here, I want to referr to a state that still wasn't defined!>);

The way I would solve this is for every action I record I would setup any type of container like an array to hold a list of actions. Then I would record the current action but delay adding it to the container. When I record the 2nd action I would then add that to the previous action, and push the previous action onto the array and then hold the current action.

When you get to the execute call you would the push the final action that's not on the container along with an action that defines the end of the FSM.

So you would have something like this

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();    
}
Chris Marisic