2

I am trying to figure out what is the best way to setup a FSM and also keep my interfaces clean and use an IOC container to use DI.

Let's just visit the Head First design pattern textbook example of the gumball machine.

interface IState
{
    InsertQuarter();
    EjectQuarter();
    TurnCrank();
    Dispense();
}

Is the best practice to just thrown an exception for an invalid state? Why add the state if it's not even applicable? I guess that is the portion where I am confused. I could see this simple example getting more complex.

My other related question is, how do I perform DI to these states? Should a state machine and states should be registered in my IOC container? Let's suppose I have an Order Service and an Inventory Service that have a dependency on a StageOrderState. What might this object model look like? I'm having a hard time digesting how to actually inject the dependencies in to these states. My guess is that I could register the states and pass the container around to the states.

  • I really dont think FSM and DI fits together. With FSM inputs, outputs, states and transitions are tightly bound. You can't really abstract them away. – Euphoric Oct 15 '13 at 16:29
2

It seems to me that you want to genericize the Head First example, so that it can accept an arbitrary collection of actions.

In C# you can have first-class functions, which means that you can pass functions to your finite state machine and have it maintain those functions in a list. Something like:

interface IState
{
    void AddStep(Action<T> action);
}

And then just pass your function to the AddStep method for insertion into the list by the state machine.

public class StateMachine : IState
{
    List<Action<T>> actions;

    public void AddStep(Action<T> action)
    {
        actions.Add(action);
    }
}

Then you just add your actions.

var machine = new StateMachine();
machine.AddStep(x => Console.WriteLine(x.ToString()));
0

For the first question: the states are not invalid in general. The actions only make sense in certain states. For example, calling EjectQuarter() will not make sense if you haven't previously called InsertQuarter(). If that happens, how do you want to proceed? Deep inside business logic, throwing an exception makes sense (there is no reasonable way to proceed); catch that exception and display an error message up in the UI layer.

For your second question, I don't think DI is meant for states (values); it's meant to inject services that other services depend on. You can inject an entire FSM into an Inventory Service; if you have methods that should behave differently depending on the state a FSM is in, either pass in the state to those methods, or pass in the FSM to the Inventory Service and let it ask the FSM about its current state. (I recommend the first option - I see no reason for an Inventory Service to know what an FSM is.)

  • If I press the eject button on a vending machine before inserting a quarter, that is not exceptional -- I do it all the time, sometimes you get a free quarter! -- and the correct action is well-defined -- don't give Eric a quarter. I don't see why that should be modeled as an exception. – Eric Lippert Oct 22 '13 at 14:48
  • Ah... my mental model was that of the inner workings of the machine - EjectQuarter always ejects a quarter. I wasn't thinking of the UI. In my model, the FSM throws, but that exception is caught by the UI layer with the same final result (no quarter is dispensed). I was led to this model by the presence of the Dispense call, which (I believe) makes no sense from an UI point of view, but does makes sense from an "inner workings of the machine" point of view. – Marcel Popescu Oct 22 '13 at 14:53
  • 1
    Ah, I see your point. I am not familiar with the example from the book but the snippet shown here seems confusing. InsertQuarter is an action taken by the customer using the machine but Dispense is an action taken by the machine. Why should both these things be methods of the state interface? This example makes very little sense to me; perhaps I should read the book. – Eric Lippert Oct 22 '13 at 14:58
  • I will admit that I am also guessing (I haven't read the book either). – Marcel Popescu Oct 22 '13 at 15:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.