ifandelse / machina.js

js ex machina - finite state machines in JavaScript
http://machina-js.org/
Other
1.93k stars 147 forks source link

Multiple handler catch #79

Closed javaknight closed 9 years ago

javaknight commented 9 years ago

Is it possible to have a handler that matches more than one state? Example, if I want a handler that matches "red" or "blue", and another that matches "yellow" or "green"

states: { myState: "red blue" : function() { .. red blue.. } "yellow green" : function() { ... yellow green }

ifandelse commented 9 years ago

@javaknight In your description above, it sounds like you mean you want handlers of the same name to be possible in different states, but your code example shows a single state object with handler names that match more than one input type. It might help to clarify the language around machina to make sure we're on the same page. In this snippet, the states are on and off, and the input names are turn.on and turn.off, and the input handlers are the values for "turn.on" and "turn.off":

states: {
    "on" : {
        "turn.on" : function() {
            console.log("we're already on");
        },
        "turn.off" : function() {
            this.transition("off");
         }
    },
    off: {
        "turn.on" : "on", //showing it's possible to use the state name for a handler value if all you need to do is transition
        "turn.off" : function() { 
            console.log("We're already off"); 
        }
    }
}

So - if you're asking if it's possible to have an input by the same name in multiple states, absolutely. In fact, this is a key piece of FSM theory in general. However, if you're asking if machina supports the ability to have one handler match multiple inputs by including the input names in the key, then no. machina does support a catch-all handler ("*" - see the README for more info), that will match any input passed to the FSM not explicitly named by other input handlers. The intention for the catch-all handler is to do things such as deferUntilTransition, or provide some sort of hook to log unexpected input, etc. machina doesn't stop you from putting a bunch of branching logic inside to handle multiple inputs in one call - but doing so is discouraged as it gets away from the benefits of method dispatch that machina employs, and will make testing and reasoning about the FSM more difficult.

javaknight commented 9 years ago

Ok. I understand

Basically, I'm trying to figure out a way around this

states: { myState: "red" : function() { .. 1000 lines of javascript .. }, "blue" : function() { .. the exact same 1000 lines of javascript as red's handler..} "yellow" : function() { ... 500 lines of javascript ..}, "green" : function() { ... the exact same 500 lines of javascript as yellow..},

Like the javascript switch statement, I was hoping for a "common-code" or "fallthrough", so that I don't need to duplicate code, and then maintain the duplications.

I guess one way is to define the functionality outside the FSM, but then the code becomes less readable...

nietonfir commented 9 years ago

@javaknight I wouldn't say it becomes less readable, in the contrary. By specifying your logic independently you follow the separation of concerns principle. Imagine the following class/model that defines your basic functionality:

var colorPicker = (function() {
    function _red() {}
    function _yellow() {}

    return {
        red: _red,
        blue: _red,
        yellow: _yellow,
        green: _yellow
    };
})();

That's one part of your code. The fsm then is another that simply uses it by calling the desired function, e.g. colorPicker.red.

ifandelse commented 9 years ago

@javaknight I agree with @nietonfir - and have taken a similar approach where handlers contain duplicate logic. Those cases, ideally, should be rare, but when they legitimately occur, nothing wrong with taking advantage of what the language offers - set your handler equal to a function ref. Machina invokes the handlers via apply, so the this context will still be the FSM. :smile: