I’ve noticed that state machines get mentioned a little more often than they are explained. Yet they represent a standard tool in the developer’s arsenal, albeit simpler to understand conceptually than they are to actually implement in a tidy and reusable way.
A state machine (sometimes finite state machine) is a model of states and transitions between those states.
Think of the seasons. If we wanted to convert a year into a state machine of seasons we know the name of the states would be “spring,” “summer,” “autumn/fall,” and “winter,” and the transition between them would normally be “wait three months.”
If we were to put this in simple code, we would need to:
- Enumerate the seasons;
- Relate which season transitions into which season;
- Allow the seasons to transition on a method call;
- Decide on a start season.
Here is my overly simple C# code for this requirement:
The memorable thing here is the definition of the seasons by way of enumeration types, turning what would otherwise be strings into first-class code citizens:
public enum SeasonalState { Spring, Summer, Autumn, Winter } |
Now, this is simple because the seasons work in a cycle. So the transitions are the same: You just hop to the next season. In fact, senior developers may refuse to recognize this as a state machine at all, because of how limited it is. However, it does achieve its brief. While the names of the methods and variables are written to reflect the domain (seasons) for ease of reading, note that if we invented a new season, only two lines would have to change. We’ll come back to implementation later.
We recognize that most real-world things don’t have definitive states that they enter and leave cleanly. On the other hand, things that can be modeled this way, should be.
The Blockchain Use Case
Is a blockchain a state machine? This is, after all, where many people have first seen the term used recently. We know what a block represents: the ownership of cryptocurrency and transference between parties. It is a (rather inefficient) state machine with an infinite number of states, similar though they are. It does have a current state, and it can only progress after a transaction has been correctly resolved. Ethereum seems keener to describe itself as a state machine, adding the user content as part of the state to make it a “smart chain.”
Talking of user content in a state, there is a small adjustment that we can make to the simple state machine code to make it a little more useful in general. This change allows a simple observer to subscribe to an event:
So now when we run the code, we see the seasons from the statements of a totally unrealistic farmer:
Spring is here. Spread the fertilizer! Summer is here. Cut the hay! Autumn is here. Harvest time! Winter is here. Feed the cows! Spring is here. Spread the fertilizer! Summer is here. Cut the hay!
|
AWS and Step Functions
The other notable modern use of state machines is within AWS. Called Step Functions, they can be used for orchestration in the serverless world. That is, other Lambda functions might be triggered by the coming of spring. What is interesting is the way they are represented in JSON:
{ “Comment”: “A Hello World example of the Amazon States Language”, “StartAt”: “HelloWorld”, “States”: { “HelloWorld”: { “Type”: “Pass”, “Result”: “Hello World!”, “End”: true } } } |
There is only the one state here, which seems to be both the start state as well as self-terminating.
A Typical State Machine: Television
Let us look at another straightforward, but much more typical state machine: a very basic television.
Now, this isn’t a television you have or want, but you know how it works. A big button on the front turns the TV on (to standby) and off again. Once on standby, you can switch between channels with the remote. We’ll ignore all the other things a TV has to have too.
If we stay in C#, we now need two enumeration types: one for the commands and one for the states.
public enum TVStates {OFF, STANDBY, CHANNEL1, CHANNEL2}
public enum TVCommands {TURN_ON, TURN_OFF, SWITCH_TO_1, SWITCH_TO_2, SWITCH_OFF} |
We can already see that we will have to focus on the transitions, not the states themselves.
Unlike the seasonal cycle, not all commands are applicable from any given state. We cannot turn the TV on twice in succession from the big button at the front. If I’m already on CHANNEL1, switching to CHANNEL1 would do nothing. So as we set up, we will need to add eight “when in this state, allow this command to move to that state” lines:
You could add in observers or “side effects,” as I did in the previous example. You can imagine that a timer might be started when a channel is changed, so that if the viewer falls asleep in front of a Japanese monster rampage or a cowboy showdown, the TV will switch itself off; in short the TV’s API may need to subscribe to some state transitions.
On running, you can see that the disallowed transitions are politely ignored:
TURN_ON: Now STANDBY SWITCH_TO_CHANNEL1: Now CHANNEL1 SWITCH_TO_CHANNEL2: Now CHANNEL2 SWITCH_OFF: Now STANDBY |
Conclusion
After you write a few state machines, you will be in a better position to use existing library versions — and you will no doubt want to. These have to be totally generalized, so it can look a little austere if you haven’t rolled a few of your own and nailed this important design pattern.
Feature image via Shutterstock.