State machines are anti-abstraction.
State machine code is hard to read.
State machines are anti-abstraction.
State machine code is hard to read.
State machines are anti-modular: By clearly defining the transitions from one state to another and when outputs can occur I see state machines as making it easier to develop modular applications. I don't see how it inevitably leads to non-modular code.
State machines are anti-abstraction: If you're modeling a process then some things can't be abstracted. At some level you have to deal with real world situations.
State machine code is hard to read: That's a function of the effort made to make it easy to read. It's no different than any other situation.
In general I find state machine design to be helpful in literally controlling the state of an application. By limiting what an application does and when it can do it, via an easily understood mental model, it's a straight forward design technique.
I have not seen a modular state machine yet. Perhaps it is possible, but it doesn't seem to be done very often.
> If you're modeling a process then some things can't be abstracted. At some level you have to deal with real world situations.
I don't think state machines are usually the most natural representation of a process. And very often, people use state machines to model things that don't really fit into that model.
> That's a function of the effort made to make it easy to read. It's no different than any other situation.
Perhaps I have only seen the bad apples.
For instance I worked on an app someone had build in which states were based on a multi-page html form. When I added an android app allowing offline data collection, I then had to hack around the state machine. We couldn't just create a instance of the object with the final data, we had to write code that replicated the submissions of the html form.
The final remaining "hard to read" that remains after DRY is usually a reflection of the problem domain, not the solution domain. That's a barrier you can't pass through; if you got seven states and ten events, you've got seventy things to deal with, one way or another. (Which doesn't have to mean 70 literal handlers, but does usually mean still quite a number.)
State machines force you to consider all possible state transitions. This makes sure that transitions are handled properly, or they are actively made impossible.
In the process all the bugs in the original code were revealed - literally a hundred cases unhandled or incompletely handled. The code I found had a dozen flags and enourmous runon procedures at every event entry point, totally unreadable.
SO yes I am a keen fan of state machines. But I admit they dominate your code architecture and look funky. Closures are definitely a good thing, tho I haven't yet figured out the best pattern. Its good to have all the cases laid out in front of you, with each combination of state and event commented and discussed. What would that look like using closures?
But one concrete example: I have a state machine that manages connection to a network resource that requires a negotiation process. I have some code that would like to just use that connection, without having to manage it, and there are many ways to enter this code and many things it may want to do. During the connection process, if more requests come in, the connection state machine just stacks them up as closures to be executed when the machine completes its connection. (Or, called another way, notify the callers of an error.) These closures themselves contain other ones that have the details of how to back to their state machines and send a message along that particular socket to go back out to the right client along one of several protocols. None of this is impossible without closures, but keeping the machines ignorant of how the other machines work is a lot harder without them.
OO can do it too, albeit with a different flavor. Without one of OO or closures is a pain, though.
digraph publish{
AUTHOR -> EDIT
EDIT -> PUBLISHED
}
voilla! a state diagram. What's that? you want metadata? digraph publish{
AUTHOR => EDIT [ key="SUBMIT"
buttonText="Submit to Editor"
privilegesRequired="SBMT"
]
}
et voilla. The modularity can find it's way in the code implementing the state machine actions. Not sure what "anti-abstraction" means, but I find the above pretty easy to read.Also, you get free diagrams via graphviz.
Imagine writing a state machine to handle TCP/IP connection establishment, and a state machine to handle parsing HTTP headers, and a state machine for the different things your web app can be doing. If you combine them all into one big state machine, you're going to have a huge mess. If you can write separate state-driven modules for each, interacting with each other at well-defined interfaces, you'll be much better off.
In other words, state machines, like most tools, can be abused. Don't do that!
state machines suck if: - the problem isn't inherently stateful - the stateful code is not carefully written
i think the problem is as the OP dscribes: people often write stateful code without realizing it, thus the code doesn't show explicit understanding the combinatorical number of state transitions and all the possible edge case bugs.
I disagree. A well-written state machine is largely declarative. And that's just as good as it gets for readability.