Per scaricare il convertitore di unità: Massa, visita: [tulip](https://<span translate=).co/library/apps/unit-converter--mass">Library
Per scaricare il Convertitore di unità: Volume, visita: [tulip.co/library/apps/unit-converter--volume">Library](https://<span translate=)
In questo articolo, uno dei nostri sviluppatori della libreria, Tamás, illustra le scelte di sviluppo alla base dell’app “Convertitore di unità” disponibile nella libreria.
Perché ho utilizzato un elenco di oggetti invece di una tabella
Ciao, sono Tamás di Tulip.
Quando ho realizzato questo componente "Convertitore di unità", una delle prime scelte architetturali che ho fatto è stata quella di non utilizzare una tabella. Ho invece scelto di memorizzare tutto all’interno di elenchi di oggetti inizializzati come variabili predefinite del componente.
Il ragionamento era semplice: la conversione delle unità in questo contesto si comporta come una costante matematica, non come dati operativi. Le relazioni sono puramente moltiplicative, cambiano raramente e non richiedono modifiche in fase di esecuzione, aggregazioni, filtri o query relazionali. L’introduzione di una tabella avrebbe comportato: * la creazione e la manutenzione di un’infrastruttura aggiuntiva, * la configurazione di query e di aggregations, * l’aggiunta di un ulteriore livello di dipendenza a ogni app che utilizza il componente.
Ciò avrebbe aumentato la complessità senza aumentare le funzionalità.
Quindi, invece, ho incorporato i set di dati direttamente all’interno del componente:* Set di dati di base canonici: * _lengthToBaseDefaults * _massToBaseDefaults * _volumeToBaseDefaults
- Dizionari dei sinonimi:
- _lengthUnitAliases
- _massUnitAliases
- _volumeUnitAliases
Entrambi sono definiti come valori predefiniti delle variabili. Ciò garantisce che il componente sia:* Portabile* Autonomo* Deterministico* Facile da copiare tra le app
Se il set di dati si comporta come una costante, preferisco proteggerlo all’interno del componente piuttosto che esternalizzarlo.
Perché non una tabella?
Una soluzione basata su tabelle introdurrebbe una dipendenza strutturale che questo problema non giustifica.
Le definizioni delle unità non sono dati transazionali. Sono definizioni strutturali. Trattarle come record operativi sarebbe sproporzionato rispetto al problema.
Il mio obiettivo qui era la proporzionalità architettonica: utilizzare solo l’infrastruttura strettamente necessaria per risolvere il problema.
Principio di progettazione: logica protetta, configurazione flessibile
Questo componente segue due idee fondamentali:1. Normalizzazione canonica di base 2. Tolleranza degli input tramite risoluzione dei sinonimi
Ho separato intenzionalmente la verità matematica dalla flessibilità dell’utente.* I set di dati di base canonici definiscono le relazioni matematiche.* I dizionari dei sinonimi definiscono il grado di flessibilità con cui vengono interpretati gli input dell’utente.
Entrambi i livelli fanno parte del progetto — non sono aggiunte a posteriori.
Nel modello di base canonico, ogni unità è mappata su una singola unità di riferimento all’interno del proprio dominio. Ogni conversione passa attraverso tale riferimento. Ciò crea un unico percorso di normalizzazione e garantisce che tutte le unità condividano lo stesso punto di riferimento matematico.
Unità di base dei domini
Ho fissato esplicitamente l’unità di base per ciascun dominio:* Lunghezza → m* Massa → kg* Volume → L
Si è trattato di scelte deliberate:* Sono standard del settore.* Riducono al minimo la deriva da arrotondamento.* Mantengono il sistema intuitivo per gli ingegneri.
Chiavi delle unità canoniche (unità supportate)
Tutte le mappature degli alias devono risolversi in una di queste chiavi canoniche.
- Massa (unità di base: kg) µg, mg, g, kg, t, lb, oz
- Lunghezza (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
Ogni dominio dispone di un proprio elenco di oggetti del set di dati di base. Ogni voce definisce un’unità e il relativo fattore di conversione (factorToBase).
La conversione segue sempre lo stesso schema:* Input → Base* Base → Destinazione
Nessuna ramificazione. Nessuna gestione delle coppie.
Interfaccia interna protetta
Per impedire modifiche accidentali alla logica, ho isolato le variabili principali:* _inputValue* _inputUnit* _targetUnit* _conversionResult
Il motore principale dipende esclusivamente da queste.
È circondato da due livelli di configurazione leggeri:* 01 – CONFIG. UTENTE: FASE DI INGRESSO Mappa le fonti esterne nelle variabili interne.* 03 – CONFIG. UTENTE: FASE DI USCITA Invia il risultato calcolato all’esterno.
Ciò mantiene stabile il nucleo computazionale consentendo al contempo un’integrazione flessibile.
Analisi del trigger – Come funziona MAGIC
Il trigger MAGIC protetto esegue due operazioni logiche:1. Risoluzione dei sinonimi delle unità2. Applicazione della normalizzazione canonica di base
Livello di risoluzione degli alias
Ogni dominio dispone di un dizionario dei sinonimi, ad esempio:* _lengthUnitAliases
Ogni oggetto contiene:* raw* canonical
Esempio:{ "raw": "mm", "canonical": "mm" }{ "raw": "millimeter", "canonical": "mm" }{ "raw": "m³", "canonical": "m3" }Ciò consente di mappare più formati di scrittura a un’unica chiave canonica. È importante sottolineare che non ho duplicato le voci all’interno del set di dati di base. Il livello dei sinonimi esiste esclusivamente per assorbire la variabilità degli input senza moltiplicare le definizioni matematiche.
Formula canonica di base
Una volta risolte le unità canoniche, la formula è:risultato = valoreInserito × fattoreDiRiduzione(valoreInseritoCanonico) ÷ fattoreDiRiduzione(valoreDestinazioneCanonico)
Esempio di trigger (dominio della lunghezza)
(@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 ) ) ))Strutturalmente, si tratta di una logica a più livelli:* Risolvi raw → canonico* Risolvi canonico → fattore* Normalizza tramite base
Sembra complessa, ma è volutamente inline per mantenere il motore autonomo ed eliminare gli stati intermedi.
Perché è stata scelta questa struttura
Volevo tre garanzie:* Nessuna esplosione delle coppie* Nessuna deriva di arrotondamento dovuta a percorsi multipli* Nessuna riscrittura dei trigger quando si estendono le unità
Il modello canonico di base mi offre tutte e tre queste garanzie. Quando aggiungo una nuova unità, aggiungo esattamente una voce factorToBase. Il motore non cambia. Quando supporto un nuovo formato di scrittura, aggiungo esattamente una voce alias. La matematica non cambia. Questa separazione è la decisione architettonica fondamentale.
Riutilizzo multidominio: stesso motore, diversi set di dati
Un’altra decisione deliberata: la logica del motore è indipendente dal dominio. Lunghezza, massa e volume condividono lo stesso trigger strutturale. Cambia solo la variabile del set di dati. Invece di costruire tre convertitori diversi, ho creato un unico motore canonico e ho lasciato che fosse il set di dati a definire il dominio. Questo riduce il carico cognitivo e mantiene prevedibile il comportamento dei componenti.
Perché questo è scalabile
La scalabilità avviene a livello di set di dati. L’aggiunta di unità non richiede: * la modifica delle espressioni di trigger, * l’aggiunta di rami condizionali, * la duplicazione della logica.
L’algoritmo rimane stabile. Il set di dati si evolve.
Estensione del componente
Aggiunta di una nuova unità canonica
- Estendere il set di dati _ToBaseDefaults pertinente.
- Aggiungere l'unità e factorToBase.
- Lasciare inalterato il trigger.
Aggiunta di un nuovo formato di scrittura
- Estendere il dizionario _UnitAliases pertinente.
- Aggiungere raw e la chiave canonica corrispondente.
- Lascia inalterato il dataset di base.
Questo è stato intenzionale: volevo una flessibilità intuitiva senza moltiplicare le definizioni matematiche.
Gestione degli errori (modello di ricerca sicura)
Esistono due punti di errore: 1. Unità "raw" non trovata nel dizionario degli alias; 2. Unità canonica non trovata nel set di dati di base
Se una delle due ricerche fallisce, il calcolo non viene eseguito.
Ciò garantisce: * Nessun valore obsoleto * Nessun errore di calcolo non segnalato * Comportamento deterministico
Perché non utilizzare array separati per le unità e i fattori?
Memorizzare unità e fattori in array separati creerebbe il rischio di dipendenza posizionale. Se un elenco cambia ordine e l’altro no, il sistema smette di funzionare in modo silenzioso. Memorizzando unit e factorToBase insieme all’interno di oggetti strutturati, ho eliminato tale fragilità strutturale.
Filosofia di base
Questo componente è volutamente semplice nella struttura e rigoroso nel comportamento.* La matematica è centralizzata.* Il motore è protetto.* La flessibilità è presente ai margini.
L’ho progettato in modo che la logica rimanga stabile nel tempo, mentre l’insieme delle unità supportate possa evolversi in sicurezza. Il motore non cambia. Il set di dati cresce.
Partecipa
Unisciti alla comunità per condividere miglioramenti, proporre unità aggiuntive, segnalare problemi o discutere le decisioni architetturali
