7

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?

user1936
  • 662
  • 5
  • 17

4 Answers4

17

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!

Ampt
  • 4,605
  • 2
  • 23
  • 45
9

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.
Blrfl
  • 20,235
  • 2
  • 49
  • 75
2

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

Aviv Cohn
  • 21,190
  • 31
  • 118
  • 178
0

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).