Para descargar el conversor de unidades: Masa, visita: [tulip](https://<span translate=).co/library/apps/unit-converter--mass">Library
Para descargar el «Convertidor de unidades: Masa», visita: [tulip.co/library/apps/unit-converter--volume">Library](https://<span translate=)
En este artículo, uno de nuestros desarrolladores de la biblioteca, Tamás, explica las decisiones de desarrollo que hay detrás de la aplicación de bloque de construcción «Convertidor de unidades» disponible en la biblioteca.
Por qué utilicé una lista de objetos en lugar de una tabla
Hola, soy Tamás, de Tulip.
Cuando creé este componente de conversor de unidades, una de las primeras decisiones arquitectónicas que tomé fue no utilizar una tabla. En su lugar, opté por almacenar todo en listas de objetos inicializadas como variables predeterminadas del componente.
El razonamiento era sencillo: la conversión de unidades, en este contexto, se comporta como una constante matemática, no como datos operativos. Las relaciones son puramente multiplicativas, rara vez cambian y no requieren edición en tiempo de ejecución, agregación, filtrado ni consultas relacionales. Introducir una tabla habría supuesto: * crear y mantener infraestructura adicional; * configurar consultas y aggregations; * añadir otra capa de dependencia a cada aplicación que utilice el componente.
Eso habría aumentado la complejidad sin aumentar la funcionalidad.
Así que, en su lugar, integré los conjuntos de datos directamente en el componente:* Conjuntos de datos básicos canónicos: * _lengthToBaseDefaults * _massToBaseDefaults * _volumeToBaseDefaults
- Diccionarios de sinónimos:
- _lengthUnitAliases
- _massUnitAliases
- _volumeUnitAliases
Ambos se almacenan como valores predeterminados de las variables. Esto hace que el componente sea:* Portátil* Autónomo* Determinista* Fácil de copiar entre aplicaciones
Si el conjunto de datos se comporta como una constante, prefiero protegerlo dentro del componente en lugar de externalizarlo.
¿Por qué no una tabla?
Una solución basada en tablas introduciría una dependencia estructural que este problema no justifica.
Las definiciones de unidades no son datos transaccionales. Son definiciones estructurales. Tratarlas como registros operativos sería desproporcionado respecto al problema.
Mi objetivo aquí era la proporcionalidad arquitectónica: utilizar solo la infraestructura que el problema realmente requiere.
Principio de diseño: lógica protegida, configuración flexible
Este componente se rige por dos ideas fundamentales:1. Normalización canónica de la base de datos. 2. Tolerancia en la entrada de datos mediante la resolución de sinónimos.
He separado intencionadamente la verdad matemática de la flexibilidad del usuario.* Los conjuntos de datos de base canónica definen las relaciones matemáticas.* Los diccionarios de sinónimos definen cómo se interpreta la flexibilidad de las entradas del usuario.
Ambas capas forman parte del diseño, no son elementos añadidos a posteriori.
En el modelo de base canónica, cada unidad se asigna a una única unidad de referencia dentro de su dominio. Toda conversión pasa por esa referencia. Esto crea una única ruta de normalización y garantiza que todas las unidades compartan el mismo punto de referencia matemático.
Unidades básicas de cada dominio
He fijado explícitamente la unidad base por dominio:* Longitud → m* Masa → kg* Volumen → L
Se trata de elecciones deliberadas:* Son estándares del sector.* Minimizan la desviación por redondeo.* Mantienen el sistema intuitivo para los ingenieros.
Claves de unidades canónicas (unidades admitidas)
Todas las asignaciones de alias deben resolverse en una de estas claves canónicas.
- Masa (base: kg) µg, mg, g, kg, t, lb, oz
- Longitud (base: m) mm, cm, m, km, in, ft, yd, mi
- Volumen (base: L) ml, cl, µl, mm³, cm³, l, m³, fl_oz_us, pt_us, qt_us, gal_us
Cada dominio tiene su propia lista de objetos del conjunto de datos base. Cada entrada define una unidad y su factorToBase.
La conversión sigue siempre el mismo patrón:* Entrada → Base* Base → Destino
Sin ramificaciones. Sin mantenimiento de pares.
Interfaz interna protegida
Para evitar modificaciones accidentales de la lógica, he aislado las variables principales:* _inputValue* _inputUnit* _targetUnit* _conversionResult
El motor principal depende únicamente de estas.
Lo rodean dos capas de configuración ligeras:* 01 – CONFIGURACIÓN DE USUARIO: ETAPA DE ENTRADA Asigna fuentes externas a variables internas.* 03 – CONFIGURACIÓN DE USUARIO: ETAPA DE SALIDA Envía el resultado calculado al exterior.
Esto mantiene estable el núcleo computacional al tiempo que permite una integración flexible.
Desglose del disparador: cómo funciona MAGIC
El disparador MAGIC protegido realiza dos operaciones lógicas: 1. Resuelve sinónimos de unidades. 2. Aplica la normalización canónica de base
Capa de resolución de alias
Cada dominio cuenta con un diccionario de sinónimos, por ejemplo:* _lengthUnitAliases
Cada objeto contiene: * raw * canonical
Ejemplo:{ "raw": "mm", "canonical": "mm" }{ "raw": "milímetro", "canonical": "mm" }{ "raw": "m³", "canonical": "m3" }Esto permite que múltiples formatos de escritura se asignen a una única clave canónica. Es importante destacar que no he duplicado entradas dentro del conjunto de datos base. La capa de sinónimos existe únicamente para absorber la variabilidad de las entradas sin multiplicar las definiciones matemáticas.
Fórmula canónica base
Una vez resueltas las unidades canónicas, la fórmula es:resultado = valorDeEntrada × factorALaBase(entradaCanónica) ÷ factorALaBase(objetivoCanónico)
Ejemplo de activador (dominio de longitud)
(@Variable._inputValue * array_value_at_index( map_to_number_list(@Variable._lengthToBaseDefaults , 'factorToBase'), array_index_of( map_to_text_list(@Variable._lengthToBaseDefaults , 'unit'), array_value_at_index( map_to_text_list(@Variable._lengthUnitAliases , 'canonical'), array_index_of( map_to_text_list(@Variable._lengthUnitAliases , 'raw'), @Variable._inputUnit ) ) ) ))/array_value_at_index( map_to_number_list(@Variable._lengthToBaseDefaults , 'factorToBase'), array_index_of( map_to_text_list(@Variable._lengthToBaseDefaults , 'unit'), array_value_at_index( map_to_text_list(@Variable._lengthUnitAliases , 'canonical'), array_index_of( map_to_text_list(@Variable._lengthUnitAliases , 'raw'), @Variable._targetUnit ) ) ))Estructuralmente, se trata de una lógica por capas:* Resolver «raw» → «canonical»* Resolver «canonical» → «factor»* Normalizar a través de la base
Parece denso, pero se ha diseñado así a propósito para que el motor sea autónomo y eliminar estados intermedios.
Por qué se eligió esta estructura
Quería tres garantías:* Que no se produzca una «explosión de pares»* Que no haya desviaciones por redondeo debido a múltiples rutas* Que no se reescriban los disparadores al ampliar las unidades
El modelo canónico base me ofrece las tres. Cuando añado una nueva unidad, añado exactamente una entrada factorToBase. El motor no cambia. Cuando admito un nuevo formato de escritura, añado exactamente una entrada de alias. Las matemáticas no cambian. Esa separación es la decisión arquitectónica fundamental.
Reutilización multidominio: mismo motor, conjuntos de datos diferentes
Otra decisión deliberada: la lógica del motor es independiente del dominio. La longitud, la masa y el volumen comparten el mismo disparador estructural. Solo cambia la variable del conjunto de datos. En lugar de crear tres convertidores diferentes, construí un único motor canónico y dejé que el conjunto de datos definiera el dominio. Esto reduce la carga cognitiva y mantiene predecible el comportamiento de los componentes.
Por qué esto es escalable
La escalabilidad se produce a nivel del conjunto de datos. Añadir unidades no requiere: * modificar expresiones de activación, * añadir ramificaciones condicionales, * duplicar la lógica.
El algoritmo se mantiene estable. El conjunto de datos evoluciona.
Ampliación del componente
Añadir una nueva unidad canónica
- Amplía el conjunto de datos _ToBaseDefaults correspondiente.
- Añade la unidad y factorToBase.
- No modifiques el disparador.
Añadir un nuevo formato de escritura
- Amplía el diccionario _UnitAliases correspondiente.
- Añade «raw» y la clave canónica correspondiente.
- No modifiques el conjunto de datos base.
Esto fue intencionado: quería una flexibilidad fácil de usar sin multiplicar las definiciones matemáticas.
Gestión de errores (patrón de búsqueda segura)
Existen dos puntos de fallo: 1. No se encuentra la unidad «raw» en el diccionario de alias. 2. No se encuentra la unidad canónica en el conjunto de datos base.
Si falla cualquiera de las búsquedas, el cálculo no se ejecuta.
Esto garantiza: * Ausencia de valores obsoletos * Ausencia de errores de cálculo ocultos * Comportamiento determinista
¿Por qué no utilizar matrices separadas para las unidades y los factores?
Almacenar unidades y factores en matrices separadas crearía un riesgo de dependencia posicional. Si una lista cambia de orden y la otra no, el sistema deja de funcionar de forma silenciosa. Al almacenar la unidad y factorToBase juntos dentro de objetos estructurados, he eliminado esa fragilidad estructural.
Filosofía fundamental
Este componente tiene una estructura intencionadamente sencilla y un comportamiento estricto.* Las operaciones matemáticas están centralizadas.* El motor está protegido.* La flexibilidad se encuentra en los extremos.
Lo he diseñado para que la lógica se mantenga estable a lo largo del tiempo, mientras que el conjunto de unidades admitidas puede evolucionar de forma segura. El motor no cambia. El conjunto de datos crece.
Participa
Únete a la comunidad para compartir mejoras, proponer unidades adicionales, informar de problemas o debatir decisiones arquitectónicas
