Is a finite state machine an appropriate solution for this situation?

I was writing some test code as an example where I had a washing machine class. It has a door state that should be open or closed, and also a running state, either on or off. I want to prevent the running state from changing from 'off' to 'on' when the door is 'open', and also prevent the door from being set to 'open' while the machine is 'on'.

I have this functionality rigged up with a bunch of if statements. It feels inelegant and it could quickly turn into spaghetti code if I want to add another state that puts additional conditions on the changes of other states. I wonder, is a finite state machine a solution for this situation? Would it simplify adding states and defining valid transitions?

What you are experiencing is called 'Code Smell' and is a good indicator that you might be doing something wrong. Luckily though, you saw the problem and already came up with a great solution, so there's not much for me to say here. A state machine works perfectly for what you're doing here.

Drawing it out might also help to try and find other states you might not think of (Running, spin cycle, cool down, filling), as well as allowing you to define transition requirements between states (probably don't want to let the door open during the spin cycle, but maybe the filling state allows the door to be opened to add garments).

Obviously you can be as detailed or ambiguous as you want, do as much as fits your needs!

You could implement your washing machine as a state machine, but there's just as much potential for state proliferation as there is for `if` statements if you don't do things carefully.

Working in states requires a different mindset where you think in terms of states (where you are), stimuli (what you get from the outside) and actions (what you do). In that context, let's look at some things in your model:

A running state, either on or off.

These are your two states, which I'll call `WASHING` and `STOPPED`. I'll add a third called `READY_TO_WASH`, which is a stopped-but-ready-to-run state you'll see later.

It has a door state that should be open or closed

This isn't really a state, but two stimuli that you get from a sensor on the machine, which I'll call `door_opened` and `door_closed`. Assume that when power is first applied, the washer will detect what state the door is in and send one of those stiumuli.

Prevent the door from being set to 'open' while the machine is 'on'.

This would imply that in addition to the sensor that generates stimuli when the door is opened or closed, there's also a latch that you can program to physically prevent the door from opening. Let's call the actions that control it `door_lock()` and `door_unlock()`.

Prevent the running state from changing from 'off' to 'on' when the door is 'open'

Changing between off and on is based a stimulus that comes from the outside, perhaps when the user presses corresponding buttons on the front panel. Let's call the stimuli we get from those buttons `wash_start` and `wash_stop`.

For completeness, let's also say there's a motor in the machine that does the actual washing and is controllable using actions called `motor_start()` and `motor_stop()`.

Everything above gives us a full set of information about what the washer's states are, what the parts of the washer can be told to do and what stimuli we can get from the outside. That's enough to build a state machine, which you do by looking at each state and figuring out what to do in response to each stimulus.

The `STOPPED` state (this is the "ground" state, or the state which the FSM enters when initialized):

• On entering this state: Do actions `motor_stop()` and `door_unlock()`. This puts the washer into a known, sane state.
• On `door_open`: Do nothing. We don't care if the door is open when stopped.
• On `door_closed`: transition to the `READY_TO_WASH` state.
• On `wash_start`: Do nothing. The door might be open. If it's closed, we won't be in this state anyway.
• On `wash_stop`: Do nothing. We're already stopped.

The `READY_TO_WASH` state:

• On entering this state: Do actions `motor_stop()` and `door_unlock()`. Again, this makes sure the washer is in a sane state.
• On `door_open`: Transition to the `STOPPED` state. If the door's open, we're not ready to wash.
• On `door_closed`: Do nothing. We're already ready and should have been put in this state already.
• On `wash_start`: Transition to the `WASHING` state.
• On `wash_stop`: Do nothing. We're already stopped.

The `WASHING` state:

• On entering this state: Do actions `door_lock()` and `motor_start()`.
• On `door_open`: Transition to the `STOPPED` state. We shouldn't be getting any door stimuli in this state because the latch prevents it. If the latch fails and someone pulls the door open, this will provide some safety.
• On `door_closed`: Same as `door_open`.
• On `wash_start`: Do nothing. We're already washing.
• On `wash_stop`: Transition to the `READY_TO_WASH` state. The door will still be closed, so we're technically ready to wash some more.

A Finite State Machine, implemented with the State design pattern, is a great solution to what you're trying to do - for two reasons:

1. The transitions between states are coded inside the states themselves. (They can also be coded in the washing machine class itself, but that makes it harder to add new states and transitions, and may result in long and ugly conditionals). So it's easy to keep track of the possible transitions to and from a state. For example if e.g. you want to prevent a transition from the 'off' state to the 'on' state when the door is 'open', all you have to do is inside the 'off' state, have the transition logic to the 'on' state wrapped with a conditional that checks if the door is open. No ugly long `if` statements to do this.

2. Adding new states is easy and doesn't result in growing `if` statements. All you have to do is add transition logic to the new state from the states that you want to be able to transition to this state. Much cleaner than a central place that manages all the transitions with long spaghetti code.

Good luck

A finite state machine implementation would be very adequate for the problem you describe. It is a good method for all problems with states that are allowed or not. This method helps you to check if your analysis is correct and complete.

I'm not sure if you are experienced in the field of finite state machines (and eventually digital circuit design). I like the following youtube tutorial: Methodology for Finite State Machine design

Good luck with your development. If you need libraries: I like using Foma (free C library) and FSM (Java code).