[Qt-interest] Qml & State Machines
K. Frank
kfrank29.c at gmail.com
Wed Dec 1 06:22:56 CET 2010
Hello Vlad -
On Tue, Nov 30, 2010 at 7:35 PM, <Volodimir.Burlik at nokia.com> wrote:
> Hi Frank,
>
> Two exremes:
> 1) You have only one state - basically it is event handler for all events in the system - the logic ends up in the action functions/handlers
>
> 2) You go crazy and create a state for each condition in your system. Bad, but still better to have logic co-located in one place, even big one.
In my experience, 2 can sometimes be worse that 1.
People often do write event-driven systems with nested if-then
code. It gets complicated and hard to maintain, but in practice,
people sometimes (and with pain) get it to work. But trying to
transform what I've called "extended state variables" into pure
enumerated state values can become fully unmanageable for
realistic (and not overly complicated) problems. (Among other
things, the number of states can explode exponentially.)
Of course I'm not saying that 1 is good. I think we both agree
that neither extreme is the right design choice.
> It is designer decision which logic belongs to application/component logic, which one - implementation detail of one sequence of action. That's why I used "linear-ish" term for action function.
Absolutely. And it's not always obvious what the right design
decision is. (That's why good programming is hard sometimes.)
>>> As a somewhat contrived example, consider a state machine that beeps every time you press a button one hundred times in a row. You could have one >> hundred states, and the button event causes the transition from state i to state i+1, with state 100 transitioning to the beep state. But it's a
>>> fundamentally better design to have a single processing-button state and an extended state variable, number-of-button-presses, with a guard
>>> condition that takes you to the beep state only when number-of-button-presses equals 100.
>
> Here it is:
>
> // more of a pseudo code though. Emmited properties mean absence of value or null.
>
> property int counter:0
> states: [
> ...
> function checkNumber(){ if(++counter == 100)sm.pushEvent("INT_TimeToBeep"); // or Id }
> ...
>
> This example shows that counting is hidden and it is implementation detail. However resulting event (reached 100) is there to understand logic by just reading the state machine.
Yes, this is a perfectly viable design approach. You can always
get rid of formal guard conditions by moving the logical content
of the guard conditions into actions, and have the actions emit
helper events that trigger transitions to the desired states.
However, there are a couple of disadvantages to this approach.
The first is technical: You either need to treat the helper event
specially, and process it synchronously (or, effectively equivalently,
put it on the front of the event queue), or you need additional logic
in your state machine to take care cases where external events
come in before you have processed your helper event. (For
example, how do you handle it if the button is clicked a 101st
time before your "INT_TimeToBeep" helper event has triggered
the desired transition?)
Having two kinds of events -- internally generated helper events
that are processed synchronously, and external events that
are processed asynchronously -- is a fully workable approach,
and some state-machine frameworks do it this way (generally
frameworks that discourage extended state variables, by the
way), but in my mind, it's a complication.
The second disadvantage is more philosophical: When building
an event-driven system without using state machines the flow
of control (i.e., what code gets executed in response to events)
is usually encoded in nested if-then programming. To me, the
power of state machines is that the state (including extended
state variables, if you use them) cleanly isolates everything you
need to know in order choose which transition to make (and
therefore what actions to execute) in response to an event.
The states and the allowed transitions are sometimes called
the topology of the state machine.
In our example, the counter does control part of the state
machine's topology -- in your implementation, through the
helper event. From this perspective, it really is part of the
state. Rather than view it as a mere implementation detail,
I think it is better to use extended state variables and formally
elevate the counter to part of the state, and let it directly
control the topology through formal guard conditions.
It seems to me that this design more directly encodes what
is actually going on -- the actual topology of the state machine,
and ultimately the flow of control in your program.
> ...
>
> Thanks
> Vlad
Best.
K. Frank
More information about the Qt-interest-old
mailing list