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

Modas… Ese problema en sistemas

Leía en un grupo de desarrolladores local (De Argentina) un hilo sobre el uso de EF y cómo este ha ido en desmedro de muchas aplicaciones al agregar una capa muy gruesa de procesos al código y su ejecución. Haciendo las aplicaciones lentas innecesariamente.

Dentro de esta conversación también se sugería que el nuevo EF para .Net Core es “más mejor” y no hace la cosas como el viejo EF. Aunque en lo personal piense que el nuevo EF hace las cosas como lo hacia el viejo EF cuando recién había llegado a nuestros códigos.

El punto es que cuando EF apareció la discusión era si debíamos usar EF o Nhibernate. Y que el que seguía usando Datasets no entendía nada (aunque tal vez aquí si se estaba en lo correcto. Hay un post en este blog, en algún lugar, que muestro el veneno que eran los DS).

Pero el post no es sobre lo absurdo de usar un ORM para una aplicación (Estaba escribiendo el tipo de aplicación en la que no se debería usar… Pero no se me ocurre una en la que sí se debería).

No, el post es sobre modas. Algo que creo que ya comenté alguna vez pero que no está de más volver a reiterar. Justamente encontré este post en el blog, de hace 5 años atrás, donde destacaba lo absurdo e inecesariamente complicado de las configuraciones de Unity para inyectar dependencias. Incluso esbozaba una idea de automatización solo por código (Curiosa cosa que hoy se consigue en .Net Core… Microsoft quiero mi dinero por copyrights).

Entonces, a que se debe que todo el mundo hable de X implementación o producto y quiera usarlo en sus desarrollos aunque no tengan sentido alguno? La respuesta es simple: Modas.

Sí. No importa si tu trabajo está detrás del teclado y no leas la “Para Ti”. Igual eres victima de las modas en tu rubro e igual que el lector de revistas del corazón, tu comprarás cualquier cosa que te pongan delante.

Este problema lo vemos todo el tiempo. Desde la aparición de jQuery (ojo, que me sigue pareciendo uno de los mejores a pesar de que podría ser más liviano) hasta su defenestración por los consumidores de Angular, para que estos sean defenestrados por los de React, y estos nuevamente por los de Angular + TypeScript. Ah! Y los de ES5 por los de ES6 + Babel, los que fueron abucheados por los de TypeScript a secas y los de ES6 + WebPack + Babel (aunque muchos no sepan que Babel está allí).

Más allá de la paranoia de este viejo loco. La realidad de la moda existe, y parte de la misma se debe a las empresas que la producen. Muchas de estas están en la necesidad de sacar cosas nuevas para mantenerse en las primeras planas. Muchas de estas cosas nuevas sí son mejoras, no se mal entienda, pero un gran puñado no es más que basura propuesta como una bala de plata salvadora que ha venido a revolucionar el mercado.

La otra parte de la responsabilidad está en nosotros. Los que le pegamos a las teclas. Es nuestra culpa por no darnos el tiempo para analizar el verdadero impacto de la nueva tecnología o herramienta propuesta. De tener el valor de decir “no” si la misma causará más daño. Es nuestra responsabilidad mantenernos actualizados y saber sobre lo que usamos y no aceptar algo solo porque un grupo de personas está haciendo algo de ruido.

Hace muchos años atras, en los cuarteles generales de una empresa que todos conocemos, alguien quería vendernos la idea de WPF. Desde el público una voz (no la mía claro) dijo: tengo cajeros de supermercado que usan terminales unix… Vos me estás diciendo que debo decirle a la gerencia que cambie todo el parque de computadoras solo para hacer una animación en un formulario de Windows?

Este comentario no hacia referencia a no querer progresar, no querer cambiar. Hacía referencia al sinsentido de algo que se quería, en aquel momento, imponer por moda.

En la charla sobre EF un amigo que admiro, además de por su amistad por sus conocimientos, dijo que en definitiva todo vuelve a simples mapeadores entre objetos y tablas. Algo que hemos hecho desde tiempos inmemoriables y que vuelve a resurgir en .Net Core.

Como en este ejemplo que escribía hace 7 años atrás o como este otro (Microsoft, segundo copyrights que me debés) intentando romper con la idea de pesados ORMs e inclinar la balanza a los mapeadores.

En resumen, somos víctimas de las modas y por lo tanto es importante que sepamos darnos el tiempo de analizar aquello que adoptamos para nuestros proyecto. Solo porque está haciendo ruido no quiere decir que sirva.

Asesino codename: el nido

Y acá estamos con otro de estos destructores de equipos y morales individuales.

El nido es un tipo de asesino que se caracteriza por la similitud que tiene con el comportamiento de los pájaros cuando estan cuidando a sus recién nacidos.

Estos presentan un comportamiento heredado de piar lo más fuerte posible para que, al llegar sus padres al nido con el alimento, tengan mayores posibilidades de ser alimentados. Y por supuesto, con mayor alimento más fuerte se vuelve por lo que produce más los gritos obteniendo, así, mayores recompensas alimenticias. Todo esto, claro, en desmedro del que no fue cuidado.

En las empresas pasa algo similar. En especial cuando uno ocupa cargos jerárquicos altos y pierde contacto con los diferentes equipos de trabajo.

Como los pájaros progenitores, un jefe o gerente tratará de visitar con cierta frecuencia a sus polluelos. Pero su desconexión con el grupo lo llevará a comportarse de similar forma que estas aves y sus crías: solo verán aquellos que hacen ruido.

Se acuerdan de ese que no se podía callar y que siempre tenía una opinión aunque no tuviese valor alguno? Bueno, ese es el pájaro chillón que tú (Si eres el jefe), como ave proveedora visitando el nido, escuchará y te hará creer que es el mejor del grupo.

Lamentablemente el alejamiento de las trincheras del desarrollo y tu larga estadía detras de hojas de cálculos y diagramas de Gantt hicieron que ya no tengas la capacidad de entender el dialecto técnico y los intrincados vericuetos del día a día del equipo, por lo que la reacción natural será premiar a aquel que quede resonando más en tus oídos.

Por otro lado, como miembro del nido, puedes convertirte en ese pajarraco gritón y aprovecharte de tu jefe adormilado. Pero esto minará el equipo, desmoralizará a tus pares. Recuerda que, en definitiva, el software se construye de a muchos.

De cualquier manera y como decía, este gerente puede llegar aún más lejos. Por ejemplo, sin entender lo cotidiano del equipo, llamarle la atención a aquel que no pía (Sólo porque no lo hace) aunque este polluelo sea una pieza fundamental en el equipo.

Recordemos que este comportamiento no es más que la expresión de inseguridad de aquel que no está involucrado en un equipl pero que, al mismo tiempo, tiene que responder por sus acciones. El ruidoso le da seguridad a este inseguro administrador, y como decía, incluso si lo que diga no tiene sentido.

De esta forma el esfuerzo individual para con el equipo se diluye y aquel que no ha gritado no recibe los beneficios de su superior e, incluso, puede llegar a recibir sus castigos.

Esto genera en aquel que está acostumbrado a trabajar en equipo un cortocircuito entre sus acciones (Corectas, pro equipo) y los “castigos” recibidos.

Imaginemos una persona haciendo todo correctamente pero que, tras cada paso dado, se le dice que está equivocado. Tarde o temprano esta persona se encontrará dudando de cada acción a tomar, no sabiendo si lo que hace es correcto o no para finalmente dejar de hacerlo.

En este momento el micromanagement se hará más frecuente y, con este, una validación del pensamiento original de ese jefe. O sea, esté comenzará a darse la razón sobre haber pensado que el no gritón no era un buen empleado.

Entonces, si estás a cargo de equipos de desarrollo, lo mejor que puedes hacer es mantenerte actualizado. No hace falta que sepas escribir código, pero por lo menos trata de entender que estás produciendo. En definitva saber sobre lo que produces es parte de considerar que eres competente en tu trabajo.

Otro asesino… silencioso

Sí, hay infinidad de asesinos de equipos, proyectos y, porqué no, empresas.

Entre estos asesinos, tal vez aquel que suele ser uno de los más comunes, pero que curiosamente es el que menos resalta, es el silencio. El silencio no en sentido de quedarse callado. Cerrar la boca y escuchar es bueno, muy bueno. De hecho ayuda a entender otros puntos de vista, a aprender.

No les ha pasado que trabajan con alguien que no puede dejar de hablar? Que siempre tiene algo para decir aunque no tenga ningún sentido lo que está diciendo? Alguien que siempre tiene un bocadillo por más estúpido que sea?

Bueno, ese no puede callarse, y es parte de otro mal, no necesariamente de este, así que ya hablaré de ese personaje.

En todo caso e intentando volver a la idea, el silencio al que me refiero es aquel que llamamos “problemas de comunicación”. Suele aparecer mucho en las retrospectivas (Si es que haces algo así) y, en nuestro rubro es común encontrarlo con frecuencia cuando un hispanohablante trabaja con gente de habla inglesa.

Y es en este momento, en esta particular ecuación en la que se suele confundir un problema profundo con algo tan superficial con no hablar bien otro idioma.

No. El problema de comunicación no está en el idioma usado, si no en la forma en como la comunicación fluye entre los miembros del equipo y nada o poco tienen que ver con el idioma.

Es más, todo esto no está asociado a trabajar con equipos con diferencias lingüísticas. Esto pasa dentro de equipos culturalmente idénticos, sentados todod juntos en el mismo espacio oficinesco.

La comunicación es un problema cuando se forman pequeños grupos dentro de los equipos en los que la información es retenida. Cuando no todos están al tanto de lo que pasa. Cuando no todos tienen voz en las decisiones que los afectarán.

Un caso obvio es cuando un grupo hace una estimación de producto, se lo vende al cliente y luego espera que un grupo diferente cumpla con lo pactado.

Te has encontrado en situación en la que llevas meses en una empresa y nadie te dijo quien es esa persona que te manda mails? O descubres que había un release a producción del que nadie te informó pero que resultas ser el responsable de llevarlo adelante? O te invitan a una reunión en la que el título de la misma hacía referencia a que conversaría sobre algún análisis de productos nuevos y cuando llegas a la misma todos, excepto tú, ya han tenido esa conversación y la reunión se tranforma en “lo conversado en el pasillo”?

Hay miles de ejemplos en el que la comunicación es un problema y de los gordos.

En cualquier caso hay un elemento particular que las distingue: alguien guarda conocimientos y no está dispuesto a transferirlos al destinatario correcto.

Dónde está el asesino?

Además de lo obvio. En cómo afecta esto al desempeño de los equipos. El principal síntoma de la muerte agónica es la caída en la moral de los individuos.

Esta caída se debe a que junto con la ausencia de información llegan los pedidos de conocimiento espontáneos. Esto quiere decir que se le exigirá a la persona que fue privada de la información que responda por no tenerla.

En el ejemplo del release a producción, las exigencias y críticas estarán relacionadas al “porqué no estaba preparado”, culpando al privado de información y no al privador.

La falta de comunicación es una de las peores asesinas. Es una de las más difíciles de erradicar ya que forma parte de la conducta del victimario por lo que cambiar esto requiere un esfuerzo gigante.

Asesinos de equipos

Con todos los cambios por los que he pasado recientemente se me ha hecho imposible continuar con algunos posts. Espero que se normalice todo y pueda volver a escribir. En todo caso, esta es una entrada para intentar re arrancar, desde el celular, con muchos errores.

Ya sabemos que hay malas empresas, malos managers, malos equípos, malos individuos en un equipo.

Yo los he tenido a todos y alguna vez he sido alguno de ellos. Con los años pulí muchas de mis equinas evitando seguir siendo un mal jugador en equipo. Por esto es que trataré de enumerar las lijas y limas y cinceles que he usado en este viaje.

Confianza

Uno de los primeros destructores de equipos, personas y empresas es la falta de confianza.

Es tan simple como decir: si no confías en tu equipo… Para que lo tienes?

He visto este comportamiento tóxico en muchas empresas, equipos y personas. Comportamiento que proviene, generalmente, desde un particular superior en escalafón empresarial y castiga al resto de los individuos de un equipo.

Esta desconfianza se suele ver cuando esta persona dirige, ordena, sin escuchar. No importan las propuestas, siempre estarán mal si provienen del equipo.

Suele verse también en comentarios de auto validación. Esta persona emite un juicio negativo sobre un subordinado cuando el mismo no está presente. Esta persona espera que alguien le diga: sí, estás en lo cierto.

Puede que la persona criticada requiera de atención. Pero la crítica siempre será para validar el punto de vista del generador de desconfianza. Nunca para pedir consejos en cómo solucionar el problema.

Generalmente esto se traduce en micromanagement. Una enfermedad empresarial que estupidiza hasta al más apto. Hace que la gente se transforme en un ente reactivo que espera que le digan que hacer. Que no tendrá iniciativa.

Por supuesto, no podemos culpar al afectado. Si sus iniciativas han sido sistemáticamente destruidas. Solo podremos jugar a la lotería tratando de acertar la fecha en la cuál decidirá irse de la empresa.

Soluciones

Si lees estas líneas pensando en que te daré una fórmula para acabar con este mal, estás equivocado. Si sufres de este mal seguramente te sentirás identificado. Si lo causas, buscarás una justificación de tus acciones para salvaguardar tu cerebro (Disonancia cognitiva).

Ahora, si estás comenzando con un equipo y te preocupa caer en esto, entonces sí te dejo un checklist de cosas a tener en cuenta:

  • Es tu equipo, conócelo. Conoce sus limitaciones. Ayúdalo a mejorar.
  • Si lo contrataste es por algo. Si contrataste a ese junior que luego del primer día le asignas el proyecto de IA y esperas que en tres días más te programe la Skynet… Bueno, estás muy mal. Acepta los conocimientos de las personas a tu cargo. Entiende sus tiempos.
  • Escucha. Tu no lo sabes todo. Puedes tener mucha experiencia pero no eres infalible. Permite que se expresen.
  • Acompaña. Si solo ordenas solo obtendrás individuos obedientes.
  • No fomentes el individualismo. Algo que nos mal enseñaron desde otras culturas es que el individualismo premia. Esto es un sinsentido total. Solo sociópatas se sienten bien con esto. El trabajo en nuestro rubro no llega a nada si se hace individualmente.

Hay mucho más, por supuesto. Esto es solo una pequeña mirada a esas conductas que intoxican los lugares de trabajo.

Interceptores (Continuación)

Siguiendo con la idea de los interceptores y esta pseudo AOP, finalmente pude subir el código a GitHub.

Claramente faltan algunas cosas (Minimizar por ejemplo), pero espero ir evolucionándolo.

En cualquier caso, pueden verlo aquí: https://github.com/MatiasIac/ToolPack

Como mostraba en el post anterior, algunos puntos a destacar eran la detección de dónde inyectar el código y cómo reconocerlo desde el mismo JavaScript sin tener que usar un meta lenguaje o algo que corra por fuera del mismo JavaScript.

s.prototype.sum = function (a, b, result, __interceptor) {
   console.log('Sum function called');
   console.log(a + b);
};

En este caso, cualquier variable que sea colocada en la firma de una función y que además inicie con __ sería tomada como el nombre del interceptor a ser ejecutado cuando esta función se llamase.

En la definición de los interceptores, entonces, tendríamos lo siguiente:

Interceptor.define('__interceptor')...

Definiendo el interceptor mediante su nombre y pudiendo agregar diferentes funciones anónimas, en cascada, que se ejecuten generando un resultado.

.having(function() {
   console.log('interceptor called');
   console.log(this);
   return "hola";
}, { context: { ok: true }, injectResult: true })

Si bien en este ejemplo no se manejan las variables que se pasaron originalmente a la función que intentábamos llamar, las mismas pueden ser declaradas en la firma de la función anónima o tomarlas desde arguments.

Solo como para darle algo de sabor a todo esto, opté por dar la posibilidad de configurar cada interceptor mediante un objeto de configuración. En este caso sería posible pasarle un contexto particular al interceptor, el que pudiera ser accedido mediante this, y también permiter retornar sus resultados e inyectarlos dentro de la función que finalmente es llamda.

Volviendo al código inicial, la variable result contendrá todos estos valores.

s.prototype.sum = function(a, b, result, __interceptor)

Seguramente dedique otro post a describir internamente el interceptor… pero eso vendrá luego 🙂

Interceptores y JavaScript

Primero que nada decir que estoy armando este post desde el celular. Lo que puede llevar a horrendos errores gramaticales y ortográficos. Y se debe a que aún no cuento con acceso a internet (Larga historia).

Segundo, el tema de este post. Básicamente hace bastante tiempo venía pensado cómo poder simular algo cercano a AOP pero en Javascript. Claro, no todo el compendio de AOP, o no rigurosamente, pero algo.

Ese algo, para este caso se limitaba a interceptores en las diferentes llamadas a funciones. Como en el caso de lenguajes como C# o Java, dónde es posible decorar una función para obtener un comportamiento previo a la ejecución de la misma.

Un ejemplo es el clásico caso de los controllers de un proyecto MVC. Algo como:

[HttpPost]
public void HacerAlgo() ...

En este caso el decorador hace que dicha función sea ejecutada solo si se produce una llamada de tipo POST.

Por supuesto, esto no es necesariamente AOP. Pero por lo menos nos permite encapsular comportamiento por fuera de la función y esperar el mismo cada vez que decoramos una función.

Entonces el siguiente paso a pensar es: Qué pasaría si quiero escribir un log antes de que la función sea llamada. O mejor aún: Qué pasa si quiero que un set de reglas se ejecute antes de entrar a la función, como por ejemplo, validar que los campos no estén vacios.

[Validar(variable="nombre", empty=false, throwException=true)]
public void MiFuncion(string nombre) ...

En este caso si al llamarse la función la variable nombre se encuentra vacía, entoces se produce una excepción.

Nuevamente, esto no es AOP real, pero para el ejemplo pensemos que es una aproximación.

El punto a evaluar es que podemos obtener conportamiento repetible adicional a nuestra función sin tener que repetir el mismo código dentro del cuerpo de la misma. Y si bien no nos liberamos del acoplamiento, si podemos tener algunos beneficios próximos al mismo.

Tal vez en el caso de AOP, al momento de compilar deberíamos obtener nuevas funciones con dicho código incorporado en ellas. En el ejemplo de C#, por el contrario, necesitamos de un observador que controle el comportamiento de los objetos, clases y funciones, use algún modelo de reflection e inyecte comportamiento mientras las funciones son llamadas.

Esto quiere decir que la estructura de la función no muta en base a nuestras decoraciones.

¿Pero qué pasa con Javascript?

Bueno, resulta un poco más difícil ya que los métodos reflectivos que tenemos disponibles (Hasta donde yo se) no nos permiten decorar y leer esas decoraciones. (Usuarios de React y Redux y demases abstenerse, eso tiene una capita adicional)

Pero sí podemos recrear una función de forma dinámica. Esto es: Tomar la función existente y sobre escribirla a nuestro gusto en tiempo de ejecución.

Toda esta cantaleta para llegar a esto.

En la imagen se puede ver un objeto en el que tenemos una función que recibe dos parámetros. El primero llamado p y el segundo __interceptor. Y es el segundo el que me interesa.

¿Que pasaría si este dictaminará que dicha función, al ser llamada, debiera primero ejecutar alguna acción definida en ese interceptor y luego, si todo sale bien, seguir el camino esperado?

En la imagen lo que terminé armando.

Básicamente la posibilidad de definir dicho interceptor (o varios en cadena). Que modifican el comportamiento original de la función. La sobre escriben y la dejan lista para ser usada con el nuevo código inyectado.

Como vemos en la consola. Al llamar a las diferentes funciones, antes, los interceptores son ejecutados para luego dar paso al código de las funciones originales.

Algunas aclaraciones.

El código es algo largo como para pegarlo en este mismo post por lo que trataré de colocarlo en GitHub en breve.

No lo hice en ES6 o TypeScript por falta de compiladores.

Seguro hay mejores implementaciones. Angular tiene cosas así. PrototypeJs creo que también. Pero ya saben… Es divertido hacerlas uno mismo.

Por último, una vez suba el código aprovecharé para armar otro post explicando algunas partes.

Espero que les resulte interesante 😛