Conversor de unidades

Prev Next

Para baixar o Conversor de Unidades: Massa, acesse: [tulip](https://<span translate=).co/library/apps/unit-converter--mass">Library

Para baixar o Conversor de Unidades: Massa, acesse: [tulip.co/library/apps/unit-converter--volume">Library](https://<span translate=)

Neste artigo, um de nossos desenvolvedores da biblioteca, Tamás, explica as decisões de desenvolvimento por trás do aplicativo de bloco de construção “Conversor de Unidades” disponível na biblioteca.

Por que usei uma lista de objetos em vez de uma tabela

Olá, sou o Tamás, da Tulip.

Quando criei esse componente de conversor de unidades, uma das primeiras decisões arquitetônicas que tomei foi não usar uma tabela. Em vez disso, optei por armazenar tudo em listas de objetos inicializadas como variáveis padrão do componente.

O raciocínio foi simples: a conversão de unidades, nesse contexto, se comporta como uma constante matemática, e não como dados operacionais. As relações são puramente multiplicativas, raramente mudam e não exigem edição em tempo de execução, agregação, filtragem ou consultas relacionais. Introduzir uma tabela teria significado: * criar e manter infraestrutura adicional, * configurar consultas e aggregations, * adicionar outra camada de dependência a cada aplicativo que usa o componente.

Isso teria aumentado a complexidade sem aumentar a capacidade.

Então, em vez disso, incorporei os conjuntos de dados diretamente dentro do componente:* Conjuntos de dados básicos canônicos: * _lengthToBaseDefaults * _massToBaseDefaults * _volumeToBaseDefaults

  • Dicionários de sinônimos:
    • _lengthUnitAliases
    • _massUnitAliases
    • _volumeUnitAliases

Ambos funcionam como valores padrão de variáveis. Isso mantém o componente:* Portátil* Autônomo* Determinístico* Fácil de copiar entre aplicativos

Se o conjunto de dados se comporta como uma constante, prefiro protegê-lo dentro do componente em vez de externalizá-lo.

Por que não uma tabela?

Uma solução baseada em tabela introduziria uma dependência estrutural que este problema não justifica.

Definições de unidades não são dados transacionais. São definições estruturais. Tratá-las como registros operacionais seria desproporcional ao problema.

Meu objetivo aqui era a proporcionalidade arquitetônica: usar apenas a infraestrutura que o problema realmente exige.

Princípio de projeto: lógica protegida, configuração flexível

Este componente segue duas ideias centrais:1. Normalização canônica de base; 2. Tolerância à entrada por meio da resolução de sinônimos

Separei intencionalmente a verdade matemática da flexibilidade do usuário.* Os conjuntos de dados de base canônicos definem as relações matemáticas.* Os dicionários de sinônimos definem como a flexibilidade da entrada do usuário é interpretada.

Ambas as camadas fazem parte do projeto — não são acréscimos posteriores.

No modelo de base canônica, cada unidade é mapeada para uma única unidade de referência dentro de seu domínio. Toda conversão passa por essa referência. Isso cria um único caminho de normalização e garante que todas as unidades compartilhem a mesma âncora matemática.

Unidades-base por domínio

Defini explicitamente a unidade-base por domínio:* Comprimento → m* Massa → kg* Volume → L

Essas foram escolhas deliberadas:* São padrões da indústria.* Minimizam o desvio por arredondamento.* Mantêm o sistema intuitivo para os engenheiros.

Chaves de Unidades Canônicas (Unidades Suportadas)

Todos os mapeamentos de aliases devem ser resolvidos para uma dessas chaves canônicas.

  • Massa (base: kg) µg, mg, g, kg, t, lb, oz
  • Comprimento (base: m) mm, cm, m, km, in, ft, yd, mi
  • Volume (base: L) ml, cl, µl, mm³, cm³, l, m³, fl_oz_us, pt_us, qt_us, gal_us

Cada domínio possui sua própria lista de objetos de conjunto de dados de base. Cada entrada define uma unidade e seu fator de conversão para a base (factorToBase).

A conversão sempre segue o mesmo padrão:* Entrada → Base* Base → Destino

Sem ramificações. Sem manutenção de pares.

Interface interna protegida

Para evitar modificações acidentais na lógica, isolei as variáveis centrais:* _inputValue* _inputUnit* _targetUnit* _conversionResult

O mecanismo central depende apenas delas.

Duas camadas finas de configuração o envolvem:* 01 – CONFIGURAÇÃO DO USUÁRIO: ESTÁGIO DE ENTRADA Mapeia fontes externas para variáveis internas.* 03 – CONFIGURAÇÃO DO USUÁRIO: ESTÁGIO DE SAÍDA Encaminha o resultado calculado para fora.

Isso mantém o núcleo computacional estável, ao mesmo tempo em que permite uma integração flexível.

Análise do gatilho – Como o MAGIC funciona

O gatilho MAGIC protegido realiza duas operações lógicas: 1. Resolver sinônimos de unidades 2. Aplicar normalização canônica de base

Camada de Resolução de Aliases

Cada domínio possui um dicionário de sinônimos, por exemplo:* _lengthUnitAliases

Cada objeto contém: * bruto * canônico

Exemplo:{ "bruto": "mm", "canônico": "mm" }{ "bruto": "milímetro", "canônico": "mm" }{ "bruto": "m³", "canônico": "m3" }Isso permite que vários formatos de escrita sejam mapeados para uma única chave canônica. É importante ressaltar que não dupliquei entradas dentro do conjunto de dados base. A camada de sinônimos existe exclusivamente para absorver a variabilidade das entradas sem multiplicar as definições matemáticas.

Fórmula canônica básica

Uma vez resolvidas as unidades canônicas, a fórmula é:resultado = valorDeEntrada × fatorParaBase(entradaCanônica) ÷ fatorParaBase(alvoCanônico)

Exemplo de gatilho (domínio de comprimento)

(@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 ) ) ))Estruturalmente, trata-se de uma lógica em camadas:* Resolver raw → canonical* Resolver canonical → factor* Normalizar por meio da base

Parece denso, mas foi intencionalmente incorporado para manter o mecanismo autônomo e eliminar estados intermediários.

Por que essa estrutura foi escolhida

Eu queria três garantias:* Sem explosão de pares* Sem desvio de arredondamento devido a múltiplos caminhos* Sem reescrita de gatilhos ao estender unidades

O modelo canônico de base me oferece todas as três. Quando adiciono uma nova unidade, adiciono exatamente uma entrada factorToBase. O mecanismo não muda. Quando ofereço suporte a um novo formato de escrita, adiciono exatamente uma entrada de alias. A matemática não muda. Essa separação é a decisão arquitetônica central.

Reutilização em múltiplos domínios: mesmo mecanismo, conjuntos de dados diferentes

Outra decisão deliberada: a lógica do mecanismo é independente do domínio. Comprimento, massa e volume compartilham o mesmo gatilho estrutural. Apenas a variável do conjunto de dados muda. Em vez de construir três conversores diferentes, criei um mecanismo canônico e deixei que o conjunto de dados definisse o domínio. Isso reduz a carga cognitiva e mantém o comportamento dos componentes previsível.

Por que isso é escalável

O escalonamento ocorre no nível do conjunto de dados. Adicionar unidades não requer: * modificar expressões de acionamento, * adicionar ramificações condicionais, * duplicar a lógica.

O algoritmo permanece estável. O conjunto de dados evolui.

Estendendo o componente

Adicionando uma nova unidade canônica

  • Amplie o conjunto de dados _ToBaseDefaults relevante.
  • Adicione a unidade e o factorToBase.
  • Deixe o gatilho inalterado.

Adicionando um novo formato de escrita

  • Amplie o dicionário _UnitAliases relevante.
  • Adicione “raw” e a chave canônica correspondente.
  • Deixe o conjunto de dados base inalterado.

Isso foi intencional: eu queria flexibilidade fácil de usar sem multiplicar as definições matemáticas.

Tratamento de erros (padrão de consulta segura)

Existem dois pontos de falha: 1. Unidade “raw” não encontrada no dicionário de aliases; 2. Unidade canônica não encontrada no conjunto de dados base

Se qualquer uma das pesquisas falhar, o cálculo não é executado.

Isso garante: * Ausência de valores desatualizados * Ausência de erros de cálculo ocultos * Comportamento determinístico

Por que não usar matrizes separadas para unidades e fatores?

Armazenar unidades e fatores em matrizes separadas criaria o risco de dependência posicional. Se uma lista mudar de ordem e a outra não, o sistema falha silenciosamente. Ao armazenar a unidade e o fatorToBase juntos dentro de objetos estruturados, eliminei essa fragilidade estrutural.

Filosofia central

Este componente tem uma estrutura intencionalmente simples e um comportamento rigoroso.* A matemática é centralizada.* O mecanismo está protegido.* A flexibilidade está nas bordas.

Projetei-o para que a lógica permaneça estável ao longo do tempo, enquanto o conjunto de unidades suportadas pode evoluir com segurança. O mecanismo não muda. O conjunto de dados cresce.

Participe

Junte-se à comunidade para compartilhar melhorias, propor unidades adicionais, relatar problemas ou discutir decisões arquitetônicas