Cosa c'è di sbagliato nelle corde magiche?


164

Come esperto sviluppatore di software, ho imparato a evitare le stringhe magiche.

Il mio problema è che è da tanto tempo che non li uso, ho dimenticato la maggior parte dei motivi. Di conseguenza, ho problemi a spiegare perché sono un problema per i miei colleghi meno esperti.

Quali ragioni oggettive ci sono per evitarle? Quali problemi causano?


38
Cos'è una corda magica? Stessa cosa dei numeri magici ?
Laiv

14
@Laiv: sono simili ai numeri magici, sì. Mi piace la definizione su deviq.com/magic-strings : "Le stringhe magiche sono valori di stringa specificati direttamente all'interno del codice dell'applicazione che hanno un impatto sul comportamento dell'applicazione.". (La definizione su en.wikipedia.org/wiki/Magic_string non è affatto ciò che ho in mente)
Kramii,

17
è divertente, ho imparato a detestare ... dopo Quali argomenti posso usare per convincere i miei ragazzi ... La storia infinita :-). Non proverei a "convincere" che preferirei lasciarli imparare da soli. Nulla dura più di una lezione / idea raggiunta dalla tua stessa esperienza. Quello che stai cercando di fare è indottrinare . Non farlo a meno che tu non voglia una squadra di Lemmings.
Laiv

15
@Laiv: mi piacerebbe che le persone imparassero dalla propria esperienza, ma sfortunatamente non è un'opzione per me. Lavoro in un ospedale finanziato con fondi pubblici in cui bug sottili possono compromettere l'assistenza ai pazienti e dove non possiamo permetterci costi di manutenzione evitabili.
Kramii,

6
@DavidArno, è esattamente quello che sta facendo ponendo questa domanda.
user56834

Risposte:


211
  1. In una lingua che viene compilata, il valore di una stringa magica non viene verificato al momento della compilazione . Se la stringa deve corrispondere a un modello particolare, è necessario eseguire il programma per garantire che si adatti a quel modello. Se hai usato qualcosa come un enum, il valore è almeno valido al momento della compilazione, anche se potrebbe essere il valore sbagliato.

  2. Se una stringa magica viene scritta in più punti, è necessario cambiarli tutti senza alcuna sicurezza (come errore durante la compilazione). Questo può essere contrastato dichiarandolo in un solo posto e riutilizzando la variabile, però.

  3. I refusi possono diventare gravi bug. Se hai una funzione:

    func(string foo) {
        if (foo == "bar") {
            // do something
        }
    }
    

    e qualcuno digita accidentalmente:

    func("barr");
    

    Questo è peggio, più la stringa è rara o complessa, specialmente se si hanno programmatori che non hanno familiarità con la lingua madre del progetto.

  4. Le stringhe magiche raramente si documentano da sole. Se vedi una stringa, non ti dice nulla di cos'altro potrebbe / dovrebbe essere la stringa. Probabilmente dovrai esaminare l'implementazione per essere sicuro di aver scelto la stringa giusta.

    Questo tipo di implementazione presenta delle perdite , che richiedono documentazione esterna o accesso al codice per capire cosa dovrebbe essere scritto, soprattutto perché deve essere perfetto per i caratteri (come al punto 3).

  5. A parte le funzioni "trova stringa" negli IDE, esiste un numero limitato di strumenti che supportano il modello.

  6. Per coincidenza potresti usare la stessa corda magica in due punti, quando in realtà sono cose diverse, quindi se hai fatto un Trova e sostituisci e hai cambiato entrambi, uno di essi potrebbe rompersi mentre l'altro ha funzionato.


34
Per quanto riguarda il primo argomento: TypeScript è un linguaggio compilato in grado di controllare i letterali delle stringhe. Ciò invalida anche l'argomento da due a quattro. Pertanto, il problema non sono le stringhe, ma l'utilizzo di un tipo che consente troppi valori. Lo stesso ragionamento può essere applicato all'uso di numeri magici per le enumerazioni.
Yogu,

11
Dal momento che non ho esperienza con TypeScript, rimanderò al tuo giudizio lì. Quello che direi quindi è che le stringhe deselezionate (come nel caso di tutte le lingue che ho usato) sono il problema.
Erdrik Ironrose,

23
@Yogu Typescript non rinominerà tutte le tue stringhe per te se cambi il tipo letterale di stringa statica che ti aspetti. Riceverai errori di compilazione per aiutarti a trovarli tutti, ma questo è solo un miglioramento parziale su 2. Non dire che è niente di meno che assolutamente incredibile (perché è quello, e adoro la funzionalità), ma sicuramente non lo fa eliminare definitivamente il vantaggio di enumerazioni. Nel nostro progetto, quando usare enum e quando non rimanere una sorta di domanda aperta di cui non siamo sicuri; entrambi gli approcci hanno fastidi e vantaggi.
KRyan,

30
Uno grande che ho visto non per le stringhe tanto quanto i numeri, ma potrebbe accadere con le stringhe, è quando hai due valori magici con lo stesso valore. Quindi uno di loro cambia. Ora stai attraversando il codice cambiando il vecchio valore con il nuovo valore, che funziona da solo, ma stai anche facendo un lavoro EXTRA per assicurarti di non cambiare i valori sbagliati. Con variabili costanti, non solo non devi affrontarlo manualmente, ma non ti preoccupi di aver cambiato la cosa sbagliata.
corsiKa

35
@Yogu Direi inoltre che se il valore di un letterale di stringa viene verificato al momento della compilazione, cessa di essere una stringa magica . A quel punto è solo un normale valore const / enum che sembra essere scritto in modo divertente. Vista questa prospettiva, direi che il tuo commento supporta effettivamente i punti di Erdrik, piuttosto che confutarli.
GrandOpener,

89

Il vertice di ciò che le altre risposte hanno colto non è che i "valori magici" sono cattivi, ma che dovrebbero essere:

  1. definito riconoscibilmente come costanti;
  2. definito una sola volta all'interno dell'intero dominio di utilizzo (se architettonicamente possibile);
  3. definiti insieme se formano un insieme di costanti che sono in qualche modo correlate;
  4. definito a un livello adeguato di generalità nell'applicazione in cui sono utilizzati; e
  5. definito in modo tale da limitarne l'uso in contesti inappropriati (es. suscettibili di verifica del tipo).

Ciò che distingue tipicamente le "costanti" accettabili dai "valori magici" è una violazione di una o più di queste regole.

Usate bene, le costanti ci permettono semplicemente di esprimere certi assiomi del nostro codice.

Il che mi porta ad un ultimo punto, che un uso eccessivo di costanti (e quindi un numero eccessivo di ipotesi o vincoli espressi in termini di valori), anche se altrimenti rispetta i criteri di cui sopra (ma soprattutto se si discosta da essi), può implicare che la soluzione che viene elaborata non sia sufficientemente generale o ben strutturata (e quindi non stiamo più parlando dei pro e dei contro delle costanti, ma dei pro e dei contro di un codice ben strutturato).

Le lingue di alto livello hanno strutture per modelli in lingue di livello inferiore che dovrebbero impiegare costanti. Gli stessi schemi possono essere utilizzati anche nella lingua di livello superiore, ma non dovrebbero esserlo.

Ma quello può essere un giudizio di esperti basato su un'impressione di tutte le circostanze e su come dovrebbe apparire una soluzione, e esattamente come questo giudizio sarà giustificato dipenderà fortemente dal contesto. In effetti potrebbe non essere giustificabile in termini di alcun principio generale, se non per affermare che "Sono abbastanza vecchio da aver già visto questo tipo di lavoro, con cui ho familiarità, fatto meglio"!

EDIT: dopo aver accettato una modifica, rifiutata un'altra e aver ora eseguito la mia modifica, posso ora considerare lo stile di formattazione e punteggiatura del mio elenco di regole da sistemare una volta per tutte ahah!


2
Mi piace questa risposta. Dopo tutto "struct" (e ogni altra parola riservata) è una stringa magica per il compilatore C. Esistono modi buoni e cattivi di codificare per loro.
Alfred Armstrong,

6
Ad esempio, se qualcuno vede "X: = 898755167 * Z" nel tuo codice, probabilmente non saprà cosa significhi, e ancor meno probabilmente saprà che è sbagliato. Ma se vedono "Speed_of_Light: numero intero costante: = 299792456" qualcuno lo cercherà e suggerirà il valore corretto (e forse anche un tipo di dati migliore).
WGroleau

26
Alcune persone mancano completamente il punto e scrivono COMMA = "," invece di SEPARATOR = ",". Il primo non rende nulla di più chiaro, mentre il secondo indica l'uso previsto e consente di modificare il separatore in un secondo momento.
marcus,

1
@marcus, davvero! Esiste ovviamente il caso di utilizzare sul posto semplici valori letterali: ad esempio, se un metodo divide un valore per due, può essere più chiaro e più semplice scrivere semplicemente value / 2, piuttosto che value / VALUE_DIVISORcon quest'ultimo definito come 2altrove. Se intendevi generalizzare un metodo di gestione dei CSV, probabilmente vorresti che il separatore fosse passato come parametro e non definito affatto come costante. Ma è tutta una questione di giudizio nel contesto: l'esempio di @GrGreau SPEED_OF_LIGHTè qualcosa che vorresti nominare esplicitamente, ma non tutti i letterali ne hanno bisogno.
Steve,

4
La risposta migliore è migliore di questa risposta se è necessario convincere che le stringhe magiche sono una "cosa negativa". Questa risposta è migliore se sai e accetti che sono una "cosa cattiva" e hai bisogno di trovare il modo migliore per soddisfare le esigenze che soddisfano in modo sostenibile.
corsiKa

34
  • Sono difficili da rintracciare.
  • La modifica di tutto può richiedere la modifica di più file in possibilmente più progetti (difficile da mantenere).
  • A volte è difficile dire quale sia il loro scopo semplicemente osservando il loro valore.
  • Nessun riutilizzo.

4
Che cosa significa "nessun riutilizzo"?
ciao

7
Invece di creare una variabile / costante ecc. E riutilizzarla in tutto il tuo progetto / codice, stai creando una nuova stringa in ognuna che provoca una duplicazione non necessaria.
Jason,

Quindi i punti 2 e 4 sono uguali?
Thomas,

4
@ThomasMoors No, sta parlando del modo in cui devi costruire una nuova stringa ogni volta che vuoi usare una stringa magica già esistente , il punto 2 riguarda la modifica della stringa stessa
Pierre Arlaud,

25

Esempio di vita reale: sto lavorando con un sistema di terze parti in cui "entità" sono memorizzate con "campi". Fondamentalmente un sistema EAV . Poiché è abbastanza facile aggiungere un altro campo, puoi accedervi utilizzando il nome del campo come stringa:

Field nameField = myEntity.GetField("ProductName");

(nota la stringa magica "ProductName")

Ciò può comportare diversi problemi:

  • Devo fare riferimento alla documentazione esterna per sapere che esiste anche "ProductName" e la sua ortografia esatta
  • Inoltre, devo fare riferimento a quel documento per vedere qual è il tipo di dati di quel campo.
  • I Typos in questa stringa magica non verranno catturati fino a quando questa riga di codice non verrà eseguita.
  • Quando qualcuno decide di rinominare questo campo sul server (difficile mentre si impedisce la perdita dei dati, ma non impossibile), non riesco facilmente a cercare nel mio codice per vedere dove dovrei modificare questo nome.

Quindi la mia soluzione era quella di generare costanti per questi nomi, organizzate per tipo di entità. Quindi ora posso usare:

Field nameField = myEntity.GetField(Model.Product.ProductName);

È ancora una costante di stringa e si compila esattamente nello stesso binario, ma presenta diversi vantaggi:

  • Dopo aver digitato "Modello", il mio IDE mostra solo i tipi di entità disponibili, quindi posso selezionare facilmente "Prodotto".
  • Quindi il mio IDE fornisce solo i nomi dei campi disponibili per questo tipo di entità, anch'essi selezionabili.
  • La documentazione generata automaticamente mostra qual è il significato di questo campo più il tipo di dati utilizzato per archiviare i suoi valori.
  • A partire dalla costante, il mio IDE può trovare tutti i luoghi in cui viene utilizzata quella costante esatta (al contrario del suo valore)
  • Typos verrà catturato dal compilatore. Questo vale anche quando si utilizza un modello nuovo (possibilmente dopo aver rinominato o eliminato un campo) per rigenerare le costanti.

Avanti sul mio elenco: nascondi queste costanti dietro a classi fortemente tipizzate generate - quindi anche il tipo di dati è protetto.


+1 porti molti punti
positivi

Se alcune parti del tuo tipo di entità sono abbastanza statiche che vale la pena definire un nome costante per esso, penso che sarebbe più appropriato definire un modello di dati adeguato per farlo, così puoi semplicemente farlo nameField = myEntity.ProductName;.
Sdraiati Ryan il

@LieRyan: è stato molto più semplice generare costanti semplici e aggiornare i progetti esistenti per usarle. Detto questo, sto lavorando per generare tipi statici in modo da poterlo fare esattamente
Hans Ke sting

9

Le stringhe magiche non sono sempre cattive , quindi questo potrebbe essere il motivo per cui non puoi trovare un motivo generale per evitarle. (Per "stringa magica" suppongo che intendi letterale stringa come parte di un'espressione e non definita come costante.)

In alcuni casi particolari, le stringhe magiche dovrebbero essere evitate:

  • La stessa stringa appare più volte nel codice. Ciò significa che potresti avere un errore di ortografia in uno dei luoghi. E sarà una seccatura delle modifiche alla stringa. Trasforma la stringa in una costante e eviterai questo problema.
  • La stringa può cambiare indipendentemente dal codice in cui appare. Per esempio. se la stringa è un testo visualizzato all'utente finale, probabilmente cambierà indipendentemente da qualsiasi cambiamento logico. Separare tale stringa in un modulo separato (o configurazione esterna o database) renderà più semplice il cambio indipendente
  • Il significato della stringa non è evidente dal contesto. In tal caso, l'introduzione di una costante renderà il codice più semplice da comprendere.

Ma in alcuni casi, le "corde magiche" vanno bene. Supponi di avere un semplice parser:

switch (token.Text) {
  case "+":
    return a + b;
  case "-":
    return a - b;
  //etc.
}

Non c'è davvero nessuna magia qui, e nessuno dei problemi sopra descritti si applica. Non ci sarebbe alcun beneficio da definire da parte di IMHO, string Plus="+"ecc. Semplificare.


7
Penso che la tua definizione di "stringa magica" sia insufficiente, deve avere qualche concetto di nascondersi / oscurare / rendere misterioso. Non vorrei riferirmi a "+" e "-" in quel contro-esempio come "magia", non più di quanto farei riferimento allo zero come magia in if (dx != 0) { grad = dy/dx; }.
Rupe,

2
@Rupe: sono d'accordo, ma l'OP usa la definizione " valori stringa specificati direttamente nel codice dell'applicazione che hanno un impatto sul comportamento dell'applicazione ", che non richiede che la stringa sia misteriosa, quindi questa è la definizione che uso in la risposta.
Jacques B

7
Con riferimento al tuo esempio, ho visto le istruzioni switch che hanno sostituito "+"e "-"con TOKEN_PLUSe TOKEN_MINUS. Ogni volta che l'ho letto mi è sembrato più difficile leggere ed eseguire il debug per questo! Sicuramente un posto in cui concordo sul fatto che usare stringhe semplici sia meglio.
Cort Ammon,

2
Sono d'accordo sul fatto che ci sono momenti in cui le stringhe magiche sono appropriate: evitarle è una regola empirica e tutte le regole empiriche hanno eccezioni. Speriamo che quando saremo chiari sul perché possano essere una brutta cosa, saremo in grado di fare scelte intelligenti, piuttosto che fare le cose perché (1) non abbiamo mai capito che può esserci un modo migliore, o (2) noi è stato detto di fare le cose in modo diverso da uno sviluppatore senior o da uno standard di codifica.
Kramii,

2
Non so cosa sia "magico" qui. A me sembrano letterali stringhe di base.
tchrist

6

Per aggiungere alle risposte esistenti:

Internazionalizzazione (i18n)

Se il testo da visualizzare sullo schermo è codificato e sepolto all'interno di livelli di funzioni, avrai difficoltà a fornire traduzioni di quel testo in altre lingue.

Alcuni ambienti di sviluppo (ad es. Qt) gestiscono le traduzioni mediante la ricerca da una stringa di testo nella lingua di base nella lingua tradotta. Le stringhe magiche possono generalmente sopravvivere a questo - fino a quando non decidi di voler usare lo stesso testo altrove e ottenere un refuso. Anche allora, è molto difficile trovare quali stringhe magiche devono essere tradotte quando si desidera aggiungere il supporto per un'altra lingua.

Alcuni ambienti di sviluppo (ad esempio MS Visual Studio) adottano un altro approccio e richiedono che tutte le stringhe tradotte siano conservate in un database di risorse e rilette per la locale corrente dall'ID univoco di quella stringa. In questo caso, l'applicazione con stringhe magiche semplicemente non può essere tradotta in un'altra lingua senza importanti rilavorazioni. Lo sviluppo efficiente richiede che tutte le stringhe di testo siano inserite nel database delle risorse e che ricevano un ID univoco quando il codice viene scritto per la prima volta, e in seguito i18n è relativamente semplice. Cercare di riempire questo dopo il fatto in genere richiederà uno sforzo molto grande (e sì, ci sono stato!), Quindi è molto meglio fare le cose in primo luogo.


3

Questa non è una priorità per tutti, ma se mai vuoi essere in grado di calcolare le metriche di accoppiamento / coesione sul tuo codice in modo automatizzato, le stringhe magiche lo rendono quasi impossibile. Una stringa in un posto farà riferimento a una classe, un metodo o una funzione in un altro posto e non esiste un modo semplice e automatico per determinare che la stringa è accoppiata alla classe / metodo / funzione semplicemente analizzando il codice. Solo la struttura sottostante (angolare, ad es.) Può determinare l'esistenza di un collegamento e può farlo solo in fase di esecuzione. Per ottenere le informazioni di accoppiamento da soli, il tuo parser dovrebbe sapere tutto sul framework che stavi utilizzando, oltre e oltre la lingua di base in cui stai codificando.

Ma ancora una volta, questo non è qualcosa che interessa molto agli sviluppatori.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.