Máquinas de estado

Ayer me preguntaban cómo solucionar un problema de flujos de datos y procesos. Lo primero que se me vino a la cabeza fue: Eso es una máquina de estados!

Imaginemos por un momento un diagrama de flujos clásicos. Donde tenemos flechas que indican dirección y conectan diferentes elementos. Estos elementos representan acciones y, desde el inicio al final, los datos fluyen dejando que cada uno de esas acciones hagan algo sobre los mismos antes de pasarlos al siguiente componente.

Adicionalmente, era necesario saber que elemento estaba realizando qué y que dicho elemento conociera cualquier otro elemento que pudiese estar conectado a este para poder enviar los datos hacia ellos.

Por supuesto, hay muchas formas de implementar una máquina de estados. Un simple switch y algunas líneas de código ya son una máquina de estados pero, por supuesto, hay que hacerlo un poco más difícil 🙂

Si aún te preguntas dónde se usan estas máquinas de estado… bueno, son comunes en el desarrollo de videojuegos. Por ejemplo, cuando un personaje está realizando una acción, lo normal es que esa acción sea un estado del personaje. Saltar, correr, caminar, son estados del objeto y solo uno puede ejecutarse en un momento particular. Luego, solo se puede ir de caminar a correr como posible flujo y no de correr a agacharse sin primero caminar (Por dar un ejemplo).

El código

El código necesitaba hacerse en JavaScript (Lenguaje que amo) y debía contemplarse la posibilidad de no tener clases base. Esto quiere decir que, por ejemplo, para un nodo de la máquina de estado pueda conocer un nodo anterior o un nodo siguiente, lo lógico sería tener una clase base que implementase estos métodos y así reusarlos en cada nueva instancia de un nodo.

La máquina

    var stateMachine = {
        currentState: null,
        next: function() {
            if (this.currentState._next !== null) {
                this.currentState = this.currentState._next;
                this.currentState.process.call(this);    
            }
        },
        prev: function() {
            if (this.currentState._prev !== null) {
                this.currentState = this.currentState._prev;
                this.currentState.process.call(this);    
            }
        }
    };

La maquina de estados solo conoce el nodo o acción actual y provee dos funciones para poder moverse hacia adelante o hacia atrás (Más movimientos dependerán de nuestras necesidades).

El nodo

    var step = (function() {

        var s = function (configuration) {
            this._configuration = configuration;
            this._next = null;
            this._prev = null;
        };

        s.prototype.process = function () { };

        return s;
    }());

Una definición de “clase” clásica. Teniendo un constructor y una función que se usará para ejecutar las acciones del nodo.

Las acciones

    var firstStepFunction = function() {
        console.log(this.currentState._configuration.text);
        this.next();
    }

    var secondStepFunction = function() {
        console.log(this.currentState._configuration.text);
        this.next();
    }

    var lastStepFunction = function() {
        console.log(this.currentState._configuration.text);
        this.next();
    }

Cada función representa una acción particular para un nodo particular.

Uniendo todo

    var step1 = new step({text: "step 1"});
    step1.process = firstStepFunction;

    var step2 = new step({text: "step 2"});
    step2.process = secondStepFunction;

    var step3 = new step({text: "step 3"});
    step3.process = lastStepFunction;

    step1._next = step2;
    step2._prev = step1;
    step2._next = step3;
    step3._prev = step2;

    stateMachine.currentState = step1;
    step1.process.call(stateMachine);

En este caso, se puede crear un nuevo nodo, asociarle una acción en particular y sus siguientes o previos nodos en la cadena de ejecución.

La línea final dispara la ejecución del primer nodo, el cual se encargará de llamar a sus siguiente nodo y ejecutar todo el flujo.

En resumen. Este tipo de máquinas nos pueden ser de mucha utilidad ahorrándonos condificiones anidades y código poco mantenible. También, poder conocer el estado de un proceso. Aislar los mismos en diferentes unidades especializadas, entre otros beneficios varios.

Anuncios


Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión /  Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión /  Cambiar )

Conectando a %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.