TE
科技回声
首页24小时热榜最新最佳问答展示工作
GitHubTwitter
首页

科技回声

基于 Next.js 构建的科技新闻平台,提供全球科技新闻和讨论内容。

GitHubTwitter

首页

首页最新最佳问答展示工作

资源链接

HackerNews API原版 HackerNewsNext.js

© 2025 科技回声. 版权所有。

Designing state machines

211 点作者 adipasquale将近 8 年前

18 条评论

idbfs将近 8 年前
A paper that changed my approach to designing state machines is &quot;Statecharts: A Visual Formalism for Complex Systems&quot; by Harel (<a href="http:&#x2F;&#x2F;www.wisdom.weizmann.ac.il&#x2F;~harel&#x2F;papers&#x2F;Statecharts.pdf" rel="nofollow">http:&#x2F;&#x2F;www.wisdom.weizmann.ac.il&#x2F;~harel&#x2F;papers&#x2F;Statecharts.p...</a>).<p>Statecharts (also called hierarchical state machines) are essentially generalized state machines which allow for nesting and parallel composition of states. The &#x27;nesting&#x27; part is my favourite, since it allows one to delegate event handling logic shared by multiple states to a &#x27;parent&#x27; state, reducing code duplication.<p>The great thing about this paper is that you can glean most of its key ideas by just looking at the diagrams.
评论 #14639873 未加载
评论 #14641382 未加载
activatedgeek将近 8 年前
While I understand the importance of state machines and its direct usage in complex Business Logic Workflows (and indirectly almost everywhere), there is nothing special in this post. It is a generic comment on State Machines. Am I missing something here?
评论 #14638690 未加载
评论 #14642761 未加载
评论 #14638807 未加载
zcdziura将近 8 年前
There was an article posted here a while back on how to implement a state machine in Rust, purely using its type system to facilitate state transitions. It&#x27;s a neat concept, even if it&#x27;s a bit more verbose than what you&#x27;ll find in other languages.<p><a href="https:&#x2F;&#x2F;hoverbear.org&#x2F;2016&#x2F;10&#x2F;12&#x2F;rust-state-machine-pattern&#x2F;" rel="nofollow">https:&#x2F;&#x2F;hoverbear.org&#x2F;2016&#x2F;10&#x2F;12&#x2F;rust-state-machine-pattern&#x2F;</a>
评论 #14639553 未加载
andreineculau将近 8 年前
FWIW there is a standard to describing (finite) state machines despite the RFC being expired <a href="https:&#x2F;&#x2F;tools.ietf.org&#x2F;html&#x2F;draft-bortzmeyer-language-state-machines-01" rel="nofollow">https:&#x2F;&#x2F;tools.ietf.org&#x2F;html&#x2F;draft-bortzmeyer-language-state-...</a><p>I have used an &quot;improved&quot; version of it <a href="https:&#x2F;&#x2F;github.com&#x2F;andreineculau&#x2F;cosmogol-abnf" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;andreineculau&#x2F;cosmogol-abnf</a> when working on <a href="https:&#x2F;&#x2F;github.com&#x2F;for-GET&#x2F;http-decision-diagram" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;for-GET&#x2F;http-decision-diagram</a>
estsauver将近 8 年前
I can&#x27;t recommend Akka&#x27;s state machines enough. It&#x27;s an incredibly simple and rock solid state machines built right into the library. (<a href="http:&#x2F;&#x2F;doc.akka.io&#x2F;docs&#x2F;akka&#x2F;snapshot&#x2F;scala&#x2F;fsm.html" rel="nofollow">http:&#x2F;&#x2F;doc.akka.io&#x2F;docs&#x2F;akka&#x2F;snapshot&#x2F;scala&#x2F;fsm.html</a>) We&#x27;re already on a Play&#x2F;Scala backend so adding these was incredibly easy for us, but I think they&#x27;re one of the hidden amazing features of the akka&#x2F;scala world.
评论 #14638554 未加载
评论 #14636427 未加载
评论 #14636543 未加载
karboosx将近 8 年前
There is good tool for making diagrams (so state machines as well) called mermaid[1], just by writing simple readable text.<p>[1] <a href="https:&#x2F;&#x2F;github.com&#x2F;knsv&#x2F;mermaid" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;knsv&#x2F;mermaid</a>
评论 #14636163 未加载
评论 #14636245 未加载
troels将近 8 年前
When dealing with business processes, I feel that the evented nature of a formal fsm can be a bit off-putting. I have on several occasions used a batch-processing approach, that maintains state in a persistent record, giving many of the same characteristics as an fsm. Let&#x27;s say we have our movie from the example (pseudo-code, obviously):<p><pre><code> class MovieState &lt; Model belongs_to :movie end class InitialStep def select MovieState.where(state: &#x27;initial&#x27;) end def process(movie_state) movie_state.state = &#x27;in_production&#x27; end end class InProductionStep def select MovieState.where(state: &#x27;in_production&#x27;) end def process(movie_state) if movie_state.movie.finished_shooting? movie_state.state = &#x27;in_theaters&#x27; end end end class MovieWorkflow def initialize @steps = [] @steps &lt;&lt; InitialStep.new @steps &lt;&lt; InProductionStep.new end def run @steps.each do |step| step.select.each do |state| step.process(state) state.save end end end end </code></pre> This makes it very clear when things are happening. It&#x27;s also easy to debug and test.<p>Of course, there are a lot of limitations, most notably that it can&#x27;t deal with an async event happening. In my experience though, this can be dealt with by storing the payload of that event somewhere in the db, where the step-selector can access it on next run. One thing this model deals very well with, is delays, which is often part of business processes.<p>Any thoughts? Does this design have a more formal name?
评论 #14642785 未加载
评论 #14640547 未加载
pwm将近 8 年前
I have recently created some dead simple code to explain the concept to colleagues whom, as the article notes, were not using this very simple and effective construct at all to control state transitions in business apps.<p>As the article does not really explain how state machines work let me try to do it here so maybe you will go back to your code and put one in place :) In general, if anywhere in your code you have some property&#x2F;field called status&#x2F;state&#x2F;etc... that you update depending on events then chances are that a state machine would improve the robustness of your code.<p>Ok, so you have some states. For business apps this usually comes from business. For our dummy example we will simulate a day (using Haskell here for conciseness, but no worry, it&#x27;s trivial and you don&#x27;t need to know any Haskell to understand it):<p><pre><code> data State = Awake | Dressed | Fed | Ready | Work | Cafe | Home | Asleep </code></pre> Now most apps stop here and thus they don&#x27;t have a machinery to control how to move between these. If we were to draw a graph where vertices are the above states and edges are how we go from one to another then, without any restriction, we would implicitly get a complete graph where you can go from any state to any state. Eg. from &quot;Ready&quot; I could go straight to &quot;Asleep&quot; without &quot;Work&quot; :) Our aim is thus to restrict transitions from one state to another to transitions that actually make sense. Making sense obviously depends on the problem we are solving. For our example let&#x27;s introduce the following events:<p><pre><code> data Event = Alarm | Dress | Eat | GoToWork | Think | Yawn | Coffee | GoHome | Read | WatchTV </code></pre> Now that we have states and corresponding events we can draw a graph where vertices are states and edges are events. The graph is now explicit in that we can only move between states (vertices) via events (edges). To put this graph into code, let&#x27;s create a function that takes a pair of state and event, eg. (Asleep, Alarm) and returns a new state, eg. Awake:<p><pre><code> myDay :: (State, Event) -&gt; State </code></pre> As said above, myDay is a function that takes a (state, event) pair, which we can also call a transition, and returns a state. This function is nothing more than a lookup table, aka. transition table, where we will write down how to transition between states:<p><pre><code> myDay (Asleep, Alarm) = Awake myDay (Awake, Dress) = Dressed myDay (Awake, Eat) = Fed myDay (Dressed, Eat) = Ready myDay (Fed, Dress) = Ready myDay (Ready, GoToWork) = Work myDay (Work, Think) = Work myDay (Work, Yawn) = Cafe myDay (Cafe, Coffee) = Work myDay (Work, GoHome) = Home myDay (Home, Read) = Asleep myDay (Home, WatchTV) = Asleep myDay (_, _) = error &quot;invalid state transition&quot; </code></pre> That&#x27;s it, we have codified our graph. We explicitly stated the valid transitions and anything else is by definition invalid. We are pretty much done at this point. A state machine itself can be thought of as a generic function that takes a transition table, a transition and returns a state. This is only to decouple a specific transition table from the general machine itself, but it literally does nothing other than lookup whether the transition is in the table. If yes then it gives back the corresponding new state otherwise we get and error.<p>You can see a pretty picture of the above graph and some code here: <a href="https:&#x2F;&#x2F;github.com&#x2F;pwm&#x2F;fsm" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;pwm&#x2F;fsm</a>
评论 #14653142 未加载
评论 #14636994 未加载
评论 #14640258 未加载
Eclyps将近 8 年前
For anyone else working in ruby, I&#x27;ve grown to love the &quot;Workflow&quot; gem[1] for my state machines. It really helps to streamline things, and also has a built-in way to generate a visual of your class&#x27;s states and transitions.<p>[1] <a href="https:&#x2F;&#x2F;github.com&#x2F;geekq&#x2F;workflow" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;geekq&#x2F;workflow</a>
评论 #14636907 未加载
jugg1es将近 8 年前
Can&#x27;t recommend state machines enough. A well designed state machine makes troubleshooting straightforward and makes adding new features a breeze without refactoring much.
siddhant将近 8 年前
In case you&#x27;re using Python, I can&#x27;t recommend this library enough - <a href="https:&#x2F;&#x2F;github.com&#x2F;pytransitions&#x2F;transitions" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;pytransitions&#x2F;transitions</a> . An absolute pleasure to work with.
评论 #14638819 未加载
评论 #14639482 未加载
jbritton将近 8 年前
I typically try to avoid state machines. I like to have a function like model = reduce(model, event) where model is an environment of variable:value bindings. This function then just uses (if expr&#x2F;else if expr) to find a condition in which to process the event. Processing the event involves changing the values in the model environment. I suppose on some level the two approaches are the same, but they feel and look different to me. If the &quot;if&#x2F;else if&quot; pattern is deeply nested, then this is kind of like traversing a state machine to reach the correct state for processing the event. So in this case maybe a state machine is more efficient. However, a state machine, involves a history. You really need an external diagram. You need to see how you get to a state and how you transition out of a state. An &quot;if&#x2F;else if&quot; type approach puts all the decision making in the code right in front of me. I don&#x27;t need a diagram. The &quot;if&#x2F;else if&quot; approach is often much more concise as the expressions are a powerful modeling approach. Finally, I prefer to avoid deeply nested if&#x2F;else if&quot; conditions, because it can be a chore to traverse the tree and really understand the path taken to get to the event handling spot. If the conditions can be written as linear list of rules where each rule can be thought about in isolation, I find this easier. This often involves repeating test conditions. Just flatten the decision tree by repeating the preconditions. This is again less efficient, but if you save the results of the expression computations then it becomes a simple test of Boolean values. I like to think of this approach as a rules engine instead of a state machine. A state machine encodes information in the state diagram so that it doesn&#x27;t need to be stored in variables. The rules engine encodes all the knowledge in variables and rules.
评论 #14642775 未加载
jrmiii将近 8 年前
In regard to tip 4, I&#x27;ve been using statesman[1] which allows you to store the transitions as table backed active record objects along with any contextual metadata.<p>Depending on how much auditability you need on why transitions happened, this may be preferable to papertrail.<p>[1] <a href="https:&#x2F;&#x2F;github.com&#x2F;gocardless&#x2F;statesman" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;gocardless&#x2F;statesman</a>
xtiansimon将近 8 年前
The podcast Podcast.__init__(&#x27;Python&#x27;)[1] recently interviewed the developer of Automat module [2]. The author explains when you should consider using a state machine in your Python project. I didn&#x27;t realize there were so many types of state machines, and the author explains where his module fits into this ecosystem.<p>[1]: <a href="https:&#x2F;&#x2F;www.podcastinit.com&#x2F;automat-state-machines-with-glyph-lefkowitz-episode-116&#x2F;" rel="nofollow">https:&#x2F;&#x2F;www.podcastinit.com&#x2F;automat-state-machines-with-glyp...</a><p>[2]: <a href="https:&#x2F;&#x2F;pypi.python.org&#x2F;pypi&#x2F;Automat&#x2F;0.5.0" rel="nofollow">https:&#x2F;&#x2F;pypi.python.org&#x2F;pypi&#x2F;Automat&#x2F;0.5.0</a>
agopaul将近 8 年前
What a coincidence, I&#x27;ve been working on a PHP state machine implementation myself in the last few days.<p>Anyway, I find the concept of &quot;events&quot; a bit weird in this case and I prefer to work with &quot;states&quot; only (state transitions rather than events that change the state). It seems like an unnecessary abstraction to think in terms of an event rather than a state.
评论 #14637919 未加载
sandGorgon将近 8 年前
What about the DB side of things ? How do you model the state graph in the DB ?<p>I have been thinking of using Postgresql CTE to do this. When a cycle occurs, I just create a new step to the dB.<p>Anyone from CRM startups who have interesting learnings here ?
评论 #14637122 未加载
评论 #14637132 未加载
Olap84将近 8 年前
Anyone got tips for working with FSMs in REST and&#x2F;or MVC? Very rarely do you find HTTP verbs match FSM events leading to a fudge somewhere
评论 #14637851 未加载
starjockey将近 8 年前
yes,and now skip all the bs and grab yourself a real tool <a href="http:&#x2F;&#x2F;twysf.users.sourceforge.net&#x2F;#A6" rel="nofollow">http:&#x2F;&#x2F;twysf.users.sourceforge.net&#x2F;#A6</a> with a real manual <a href="http:&#x2F;&#x2F;twysf.users.sourceforge.net&#x2F;ccide_man.shtml" rel="nofollow">http:&#x2F;&#x2F;twysf.users.sourceforge.net&#x2F;ccide_man.shtml</a> .combine this with m4 and generate efficient working code for luajit and c.and as a bonus you learn m4,a macro processor that can take output from cli programs and insert it inside your source code.much more pragmatic than lisp