DEV Community

Cover image for De if/switch a mapped functions (a.k.a. object lookup)
Dailos Rafael D铆az Lara
Dailos Rafael D铆az Lara

Posted on

De if/switch a mapped functions (a.k.a. object lookup)

馃嚞馃嚙 English version

馃幆 Objetivo

Este texto est谩 orientado a proporcionar una alternativa para aquellas situaciones donde nuestro c贸digo debe estructurarse para ejecutar una u otra funci贸n, dependiendo de un conjunto definido de posibles condiciones.

En ning煤n momento es mi intenci贸n criticar el uso de if/else o de switch/case. Mi 煤nico objetivo es proporcionar una propuesta diferente con que mejorar el mantenimiento y la escalabilidad de nuestro c贸digo.

Una vez dicho esto... empecemos!!!

馃摎 Instrucci贸n if/else

Desde que empezamos a aprender a programar, el primer control de flujo de informaci贸n que aprendemos es el if/else (MDN if/else documentation). De este modo, cuando ya lo hemos aprendido, es realmente f谩cil utilizarlo.

Incluso cuando la cantidad de posibles opciones se incrementa, podemos encadenar multiples if/else.

Adem谩s, cuando tenemos varias opciones que deben tratarse de la misma manera, es decir que comparten la misma l贸gica de negocio, podemos usar operadores booleanos (el OR en este caso), para agrupar todas esas opciones bajo el mismo bloque de c贸digo.

Todo esto est谩 genial pero cuando los posibles casos superan las dos o tres opciones, el c贸digo empieza a parece un poco sucio.

Pros (鉁) y contras (馃憥)

鉁 Es la manera m谩s f谩cil de controlar el flujo de informaci贸n.
鉁 Es relativamente f谩cil de aprender.
鉁 discriminar entre dos posibles opciones es realmente c贸modo.

馃憥 Cuando gestionamos m谩s de tres opciones, el c贸digo empieza a parece un poco sucio..
馃憥 Encadenar m煤ltiples opciones disminuye la legibilidad y el mantenimiento de nuestro c贸digo.
馃憥 Agrupar opciones empleando operadores booleanos puede hacer m谩s complicadas las reglas de comparaci贸n en cada situaci贸n.
馃憥 Para una cantidad relativamente grande de casos posibles, es m谩s lento ya que cada condici贸n deber ser comprobada hasta alcanzar aquella que coincida con el valor de referencia proporcionado.

馃 Instrucci贸n switch/case

Cuando queremos mejorar la legibilidad y mantenimiento de nuestro c贸digo debido a que tenemos m煤ltiples opciones que gestionar, es cuando aprendemos la alternativa al if/else, es decir, el switch/case (MDN switch/case documentation).

De la misma manera que hac铆amos con el if/else, con el switch/case tambi茅n podremos agrupar opciones pero en este caso, no necesitamos utilizar ning煤n operador booleano. S贸lo necesitamos mantener unidos los diferentes casos a agrupar.

Como ya sabr谩s, esto es posible gracias a que la ejecuci贸n del switch/case es un proceso secuencial, donde cada posible caso definido en el conjunto de opciones, es comparado con la referencia proporcionada.

Si ambos valores coincide, el bloque de c贸digo incluido en ese caso se ejecuta y, si no hay una instrucci贸n break o return al final de dicho bloque de c贸digo, el siguiente caso ser谩 comprobado hasta encontrar la pr贸xima coincidencia o hasta llegar al bloque default.

Bas谩ndonos en esto, para agrupar m煤ltiples opciones las cuales van a ser gestionadas por el mismo bloque de c贸digo, s贸lo necesitamos definir el caso para el valor deseado, sin ning煤n tipo de l贸gica de negocio. De este modo seremos capaces de encadenar m煤ltiples opciones para el mismo resultado.

Pros (鉁) y contras (馃憥)

鉁 Proporciona una mejor estructura del c贸digo que al usar instrucciones if/else.
鉁 Es posible crear agrupamiento de casos de una manera m谩s clara que con instrucciones if/else.
鉁 Es realmente sencillo discriminar entre m谩s de dos opciones.

馃憥 Tenemos que estar pendiente de completar todos los bloques de c贸digo con una instrucci贸n break o return. Si nos olvidamos de hacerlo, nos podemos meter en un buen l铆o.
馃憥 Para cantidades relativamente grandes de casos, es lento dado que cada condici贸n debe ser comprobada hasta llegar a aquella que coincide con la referencia que le hemos proporcionado.

馃敟 Mapped functions

Esta es una estrategia poco conocida (tambi茅n llamada object lookup) y est谩 destinada a mejorar determinados aspectos del uso de instrucciones if/else y switch/case.

La idea es aprovechar el comportamiento de los objetos de JavaScript para usar sus claves como mapa de referencias y acceder directamente a l贸gica de negocio espec铆fica.

Antes de nada, necesitamos tener definidos los posibles casos que van a ser gestionados.

Cada caso individual ser谩 asociado a una clave del objeto literal.

Una vez hemos creado nuestro objeto, usaremos el estilo de acceso array para ejecutar el c贸digo de cada caso individual.

Pros (鉁) y contras (馃憥)

鉁 Proporciona una estructuraci贸n del c贸digo mejor que la que obtenemos al usar instrucciones if/else y switch/case.
鉁 No hay agrupamiento de posibles casos dado que cada uno de ellos tiene definida su propia l贸gica de negocio.
鉁 Es extremadamente f谩cil diferenciar entre m煤ltiples opciones de ejecuci贸n.
鉁 Puede ser reutilizado en varias partes de nuestra aplicaci贸n (via exportaci贸n de m贸dulo).
鉁 Es m谩s r谩pido que if/else y switch/case dado que accedemos a la condici贸n espec铆fica que queremos ejecutar, sin necesitar verificar cada uno de los casos secuencialmente, hasta encontrar el correcto.

馃憥 Esta estrategia rara vez aparece en las formaciones m谩s habituales.
馃憥 Si el objeto no se define en el lugar indicado de la aplicaci贸n, puede consumir un poco m谩s de memoria de la necesaria.

馃 FAQ

鉂 驴Qu茅 sucede si proporcionamos una opci贸n que no est谩n entre las claves del objeto?

La respuesta corta es que se disparar谩 una excepci贸n ya que no es posible ejecutar una funci贸n de undefined.

No obstante, podemos prevenir esto definiendo un caso default, de la misma manera que hacemos en la instrucci贸n switch/case.

Para ser capaces de acceder a este nuevo caso, comprobaremos si la opci贸n proporcionada existe dentro del objeto y si no existe, entonces ejecutaremos la opci贸n default.

Para estos casos, operador condicional (ternario) ser谩 nuestro aliado.

鉂 驴Qu茅 puedo o debo devolver en el caso default?

Esto va a depender del caso de uso que estemos definiendo pero b谩sicamente, vamos a tener tres opciones principales:

1 - Devolver el mismo valor que hemos proporcionado:

2 - Devolver null o undefined:

En este caso, podemos incluso aprovechar el optional chaining y dejar m谩s limpio nuestro c贸digo:

Debemos prestar atenci贸n porque in este caso, si no hay coincidencia en las opciones disponibles, vamos a devolver undefined.

3 - Definir una l贸gica de negocio espec铆fica:

Aqu铆 debemos tener cuidado si nuestro c贸digo, como se muestra en el ejemplo, va a disparar un error. Tenemos que gestionar dicho error para evitar un error total que bloquee nuestra aplicaci贸n.

Obviamente el c贸digo que implementa el error puede ser reemplazado por cualquier otra l贸gica de negocio que se adecue mejor al comportamiento de nuestra aplicaci贸n.

鉂 驴Necesito definir una funci贸n an贸nima para cada caso?

No, en absoluto.

Si tenemos perfectamente definida la funci贸n que debe ser ejecutada para cada caso y adem谩s, dicha funci贸n recibe 煤nicamente un argumento que coincide con el que estamos proporcionando cuando invocamos al mapa, podemos usar esta sintaxis:

Incluso si queremos devolver undefined cuando la opci贸n proporcionada no est谩 incluida dentro del mapa, podemos usar esta otra sintaxis extremadamente simplificada (Advertencia 鈥硷笍: todas las funciones usadas para crear las claves del mapa, han de estar definidas previamente):

鉂 驴Es posible que el nombre de una propiedad entre en conflicto con el de un objeto?

Rotundamente s铆.

Es totalmente posible, pero para evitar esto tenemos que prestar atenci贸n a los nombres que estamos usando, de la misma manera que nunca utilizar铆amos una palabra reservada del lenguaje como nombre de variable, funci贸n u objeto.

鉂 驴Esto podr铆a formar una nueva convenci贸n de nombres?

S铆, claro.

Pero para este tipo de situaciones tenemos el apoyo y las gu铆as proporcionadas por el Clean Code.

Cada c贸digo que creemos requerir谩 una convenci贸n de nombres. En algunos casos cuando seamos la 煤nica persona que ha iniciado el proyecto, podremos definir dicha convenci贸n (pet-projects principalmente). En otras situaciones, ser谩 el equipo de desarrollo el responsable de cualquier acuerdo alcanzado a tal efecto.

鉂 驴Requerir谩 un uso de memoria adicional mientras que el if/else y el switch/case no lo hacen?

S铆, lo har谩.

Sin embargo, bas谩ndonos en los tipos de dispositivos que ejecutan nuestras aplicaciones JavaScript hoy en d铆a as铆 como en sus caracter铆sticas, el incremento de memoria es pr谩cticamente insignificante en comparaci贸n con el resto de la aplicaci贸n.

鉂 驴Ser铆a esta opci贸n m谩s lenta que el if/else o el switch/case dependiendo del motor de JavaScript que se use?

Esto va a depender de c贸mo definamos el objeto en s铆.

Por ejemplo, si definimos el objeto de mapeado de funciones dentro de una funci贸n, la cual va a ser invocada cada vez que queramos hacer uso del mapa, obviamente esta soluci贸n va a ser m谩s lenta que las otras opciones, porque el objeto debe ser creado cada vez.

En este c贸digo podemos ver la situaci贸n donde la funci贸n mappedFunction tiene definido el objeto dentro de ella:

Codepen 馃憠 Speed race Switch 馃悋 vs Object Lookup 馃悽 v1

Aqu铆 no importa qu茅 motor de JavaScript estemos usando para ejecutar el c贸digo (AppleWebKit para Safari, SpiderMonkey para Firefox o V8 para Google Chrome y/o NodeJS), porque el mapeado de funciones ser谩 siempre m谩s lento (incluso si operamos los primeros casos), debido a que el objeto se est谩 creando ad-hoc en cada ejecuci贸n de la funci贸n.

Sin embargo, si definimos el mapeado de funciones de manera global (al m贸dulo o a la aplicaci贸n), el objeto se cargar谩 cuando el m贸dulo o la aplicaci贸n lo usen. De este modo, el acceso a las funciones mapeadas ser谩 siempre m谩s r谩pido que las otras dos opciones.

En este c贸digo hemos definido el mapa fuera de la funci贸n mappedFunction:

Codepen 馃憠 Speed race Switch 馃悽 vs Object Lookup 馃悋 v2

鉂 驴Qu茅 pasa con el recolector de basura?

Hoy en d铆a el recolector de basura es algo a lo que quienes desarrollamos con JavaScript no le prestamos mucha atenci贸n, debido a que est谩 ampliamente cubierto por las especificaciones del lenguaje as铆 que, una vez el mapa de funciones ya no est谩 en uso en el proceso de ejecuci贸n actual, el objeto ser谩 gestionado por el recolector de basura autom谩ticamente.

Para m谩s informaci贸n respecto a este tema, recomiendo echar un vistazo a esta documentaci贸n de la MDN relativa a la gesti贸n de la memoria.

Recursos adicionales:

馃憢 Conclusiones finales

Como ya he dicho al principio de este post, no es mi intenci贸n criticar de ning煤n modo el uso de if/else o switch/case, sino que 煤nicamente pretendo proporcionar otra manera de realizar dichas operaciones.

Resumiendo, cuando tengamos que discriminar entre dos simples opciones, es obvio que la alternativa m谩s sencilla es usar if/else. Adem谩s recomiendo encarecidamente que intentes usar el operador ternario all铆 donde sea posible.

Para aquellos casos donde tengamos que diferenciar entre tres o m谩s opciones, sinceramente recomiendo el uso de funciones mapeadas para proporcionar una mejor legibilidad, mantenimiento y reutilizaci贸n de nuestro c贸digo.

Espero que este contenido te sea 煤til. Si tienes cualquier pregunta, si茅ntete totalmente libre de contactar conmigo. Aqu铆 est谩n mis perfiles de Twitter, Linkedin y Github.

馃檹 Reconocimientos y agradecimientos

Top comments (0)