Imagine an ordinary programmer for a moment. Suppose his name is Vasya and he needs to make an animated menu on the site / desktop application / mobile app. You know, who go from top to bottom, like the menu at the Windows window or the menu with the apple in OS X. That's it.
He starts with a single drop-down window, tests the animation, sets ease out 100% and enjoys the result. But soon he realizes that in order to manage the menu, it would be good to know if it is closed now or not. We are experienced programmers here, we all understand that we need to add a flag. No question, there is a flag.
var opened = false;
It seems to work. But, if you quickly click on the button, the menu starts blinking, opening and closing without having had time to reanimate into the final state. Vasya adds a flag
animating . Now we have the code:
')
var opened = false; var animating = false; function onClick(event) { if (animating) return; if (opened) close(); else open(); }
After some time, Vasya is told that the menu can be completely turned off and inactive. No problem! We are experienced programmers here with you, we all understand that ...
we need to add ANOTHER FLAG! And, only after a couple of days of development, the menu code is already replete with two-line IF-s like this:
if (enabled && opened && !animating && !selected && finishedTransition && !endOfTheWorld && ...) { ... }
Vasya starts asking questions: how can it be that animating == true and enabled == false; why is it all buggy from time to time; How can you even understand the state of the menu? Aha
States ... About them further, and will be discussed.
Meet this Vasya.

condition
Vasya is already beginning to understand that many combinations of flags do not make sense, and the rest can be easily described with a couple of words, for example:
Disabled ,
Idle ,
Animating ,
Opened . We are all experienced programmers here, we immediately recall about state machines. But, Vasya will have to tell what it is and why. In simple language, without any mathematical terms.
We have an object, for example, the aforementioned menu. The object is always in one state and reacting to various events can move between these states. Usually, states, events and transitions are conveniently described with such schemes (circles denote initial and final states):

From the scheme it is clear that from the state
Inactive to
Active can be reached only by the
Begin event, and from the state
Paused one can get into both
Active and
Inactive . For some reason, this simple concept is called the “State Machine” or the “Finite State Machine”, which is very frightening for ordinary people.
According to the PLO covenant, the states should be hidden inside the object and just not accessible from the outside. For example, an object during operation may have 20 different states, but the external API answers the question
“how are you?” Answers
“nothing so” to 19 of them and only
foul language on 1 that
all polymers are missed .
Following the concept of state machines, it is very easy to structure the code in such a way that it will always be clear what and how an object does. It will always be clear that something went wrong if the system suddenly tried to go into a state inaccessible from this state. And events that suddenly dared to come at the wrong time, you can safely ignore and not be afraid that something will break.

The world's easiest state machine
Suppose now Vasya is doing a project in C # and he needs a simple state machine for one type of objects. He writes something like this:
private enum State { Disabled, Idle, Animating } private State state; void setState(State value) { state = value; switch (state) { case State.Disabled: ... case State.Idle: ... case State.Animating : ... break; } }
And this is how it handles events depending on the current state:
void event1Handler() { switch (state) { case State.Idle: ... break; } }
But, we are experienced programmers here, we all understand that the setState method will eventually grow into a couple of dozen pages, which (as it is written in textbooks) is not good.
State Pattern
Googling a couple of hours, Vasya decides that the
State Pattern is perfect in this situation. Moreover, senior programmers are always competing who will stuff more patterns into their app, so, Vasya decides, patterns are important.
For example, for a State Pattern, you can make an
IState interface:
public interface IState { void Event1(); void Event2(); }
And on a separate class for each state that this interface is implemented. In theory, it looks beautiful and 100% according to the textbook.
But, first, for every unfortunate petty state machine you need to fence a lot of classes, which in itself is not fast. Secondly, problems with access to shared data will begin sooner or later. Where to store them? Basically class? How do state classes get access to them? And how can I get a quick hack around the rules 15 minutes before the deadline? And similar interaction problems that will greatly slow down the development.
Implementation based on language features
Some programming languages facilitate the solution of certain problems. In Ruby, for example, so generally there is a whole DSL (and not one) for creating state machines. And in C #, the state machine can be simplified through Reflection.
Something like this :
- inherit from the class FiniteStateMachine ,
- create methods called stateName_eventName () that are automatically called when navigating through states and processing events
I really have much less code to write.
Having implemented the system described above, Vasya understands that she, too, has more disadvantages than advantages:
- It is necessary to inherit from the class FiniteStateMachine,
- In reactions to custom events, you also need to write large switch constructions,
- It is not possible to transfer parameters when the state changes.
Framework
In the meantime, Vasya already began to delve into the theory of state machines and decided that it would be nice to be able to formally describe them via the API or (oh God) via XML, which sounds cool in theory. We are experienced programmers here, we all understand that you need to write your own framework. Because
others are not suitable, since they all have one
fatal flaw .
Vasya decided that using his framework it would be possible to quickly and easily create a state machine without having to write a lot of unnecessary code. The framework will not impose any restrictions on the developer. Everyone around will be cheerful and cheerful.
I tried many frameworks in different languages, I wrote a few of these myself. And always to describe the finite state machine by means of the framework, more code was required than in a
simple example . All of them impose certain restrictions, and many are trying to do so much at once that in order to figure out how to create an uncomplicated state machine, you have to search the documentation for a long time.
Here, for example, the description of a state machine by a
stateless framework:
var phoneCall = new StateMachine<State, Trigger>(State.OffHook); phoneCall.Configure(State.OffHook) .Permit(Trigger.CallDialed, State.Ringing); phoneCall.Configure(State.Ringing) .Permit(Trigger.HungUp, State.OffHook) .Permit(Trigger.CallConnected, State.Connected); phoneCall.Configure(State.Connected) .OnEntry(() => StartCallTimer()) .OnExit(() => StopCallTimer()) .Permit(Trigger.LeftMessage, State.OffHook) .Permit(Trigger.HungUp, State.OffHook) .Permit(Trigger.PlacedOnHold, State.OnHold);
But, making your way through the creation of a state machine, you can use the useful functions that the framework provides. Basically they are: validation of transitions, synchronization of dependent state machines and sub-state machines and all kinds of protection against a fool.
XML
XML is a separate evil. Someone once thought of using it to write configs. A herd of lemming java developers for a long time praying for him. And now, no one knows why everyone uses XML, but
continues to beat everyone who tries to get rid of it .
Vasya also got an idea that you can configure everything in XML and DO NOT WRITE ANY CODE LINE! As a result, XML files of approximately the following content lie separately in its framework:
<fsm name="Vending Machine"> <states> <state name="start"> <transition input="nickel" next="five" /> <transition input="dime" next="ten" /> <transition input="quarter" next="start" action="dispense" /> </state> <state name="five"> <transition input="nickel" next="ten" /> <transition input="dime" next="fifteen" /> <transition input="quarter" next="start" action="dispense" /> </state> <state name="ten"> <transition input="nickel" next="fifteen" /> <transition input="dime" next="twenty" /> <transition input="quarter" next="start" action="dispense" /> </state> ... </states> </fsm>
Great! And no programming. But, we are experienced programmers here, we all understand that programming has not gone anywhere. Vasya replaced a piece of imperative code with a piece of declarative code, adding an XML interpreter to the framework, which still complicated a couple of times. And then try it otdebazhit when the code in different languages and scattered on the project.
Agreement
And here Vasya got tired of all this and he went back to
the simplest finite state machine in the world . He redid it a bit and came up with the rules for how to write code in it.
UPDATE: thanks for the comments. This really lacked a little explanation.We have several states. The transition between them is a transaction from atomic operations, that is, they all always happen together, in the correct order, and some other code cannot interpose between them. When the state changes from A to B, the following happens: the exit code from the state A is executed, the state changes from A to B, the entry code to state B is executed
To go to state A, you need to call the stateA method, which will execute the necessary logic and call setState (A). Calling setState (A) yourself is highly discouraged.
It turned out the following:
/** * enum , enums. */ private enum State { Disabled, Idle, Animating } /** * . , . */ private State state; /** * state< >(). * . * setState(newValue) . */ void stateDisabled() { switch (state) { case State.Idle: break; } setState(State.Disabled); // State Disabled enter logic } /** * . * stateIdle(0); */ void stateIdle(int data) { setState(State.Idle); // State Idle enter logic } void stateAnimating() { setState(State.Animating); // State Animating enter logic } /** * setState * state = value; * prevState = state; . * , . */ void setState(State value) { switch ( state ) { case State.Animating: // state Animating exit logic break; // other states } state = value; } /** * , . */ void event1Handler() { switch (state) { case State.Idle: // state Idle logic break; // other states } }
UPDATE: A unique exit logic is written to setState (), and a specific exit logic from state A is possible in stateB () when switching to B. But it is very rarely used.
A simple convention for writing state machines. It is quite flexible and has the following advantages:
- almost all logic when changing states is in stateA () methods, which allows you to break a giant switch into setState () and make the code more readable,
- state change occurs only through stateA () methods, which makes debugging easier,
- You can easily pass parameters to a new state, for example, if a book has a Page state, then you can switch to a new page simply by changing the state by calling statePage (42)
- In event handlers, it is always clear what logic is executed in which states,
- all team members know where to write logic to enter and exit the state,
- there is no need for any framework and pre-configuration of the finite state machine,
- There is an opportunity to dirty everything at the last moment, if absolutely no other way.
Another non-obvious advantage is the independence of the agreement from the language. Moving from one platform to another, you do not have to rewrite your favorite framework into another language or look for a worthy replacement for it.
As in all agreements, some code may first be in one place, but then it will have a different meaning, or it will appear that it is duplicated somewhere. Then we can move it to another place. Nobody forbids us. Still, the code is not carved out of stone, it is just a text that (oh, horror!) Can and should be changed with the development of the project.
UPDATE: and setState () may well be replaced by a single setter for clarity.Conclusion
This ends the fascinating adventure of Vasya in the world of state machines. But there is still so much interesting. A separate topic would only deserve parallel and dependent state machines.
I hope that if you are not yet using state machines everywhere, this article will drag you to the side of good; If you write your UTF to work with state machines, it will help you to take a fresh look at what you get.
I hope that this article will help developers to think about where and when to use patterns and frameworks, and that the described agreement on the design of state machines will be useful to someone.