Cosa c'è di sbagliato nelle stringhe magiche?

155

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

Il mio problema è che è passato tanto tempo da quando li ho usati, ho dimenticato la maggior parte dei motivi per cui. Di conseguenza, ho difficoltà a spiegare perché sono un problema per i miei colleghi meno esperti.

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

    
posta Kramii 05.02.2018 - 10:29
fonte

7 risposte

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

  2. Se una stringa magica viene scritta in più posizioni devi cambiarle tutte senza alcuna sicurezza (come l'errore in fase di compilazione). Questo può essere neutralizzato solo dichiarandolo in un posto e riutilizzando la variabile, però.

  3. Gli errori di battitura possono diventare seri bug. Se hai una funzione:

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

    e qualcuno digita accidentalmente:

    func("barr");
    

    Questa è la cosa peggiore è la più rara o complessa è la stringa, specialmente se hai programmatori che non hanno familiarità con la lingua nativa del progetto.

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

    Questo tipo di implementazione è leaky , che richiede una documentazione esterna o l'accesso al codice per capire cosa dovrebbe essere scritto, soprattutto perché deve essere perfetto per il personaggio (come al punto 3). / p>

  5. A corto di funzioni "trova stringa" negli IDE, c'è un piccolo numero di strumenti che supportano il modello.

  6. Potresti usare la stessa stringa magica in due punti, quando in realtà sono cose diverse, quindi se hai fatto un Trova & Sostituisci e modifica entrambi, uno dei due potrebbe interrompersi mentre l'altro funzionava.

risposta data 05.02.2018 - 11:10
fonte
88

Il vertice di ciò a cui le altre risposte hanno afferrato, non è che i "valori magici" siano cattivi, ma che dovrebbero essere:

  1. definito riconoscibilmente come costanti;
  2. definito solo una volta all'interno del loro intero dominio di utilizzo (se possibile dal punto di vista dell'architettura);
  3. definiti insieme se formano un insieme di costanti che sono in qualche modo correlate;
  4. definito ad un livello appropriato di generalità nell'applicazione in cui sono utilizzati; e
  5. definito in modo tale da limitare il loro utilizzo in contesti inappropriati (ad esempio, è possibile il controllo del tipo).

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

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

Il che mi porta ad un punto finale, 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 sopra (ma soprattutto se si discosta da loro), può implicare che la soluzione che viene escogitata non sia sufficientemente generale o ben strutturata (e quindi non stiamo parlando dei pro e dei contro delle costanti, ma dei pro e dei contro di un codice ben strutturato).

I linguaggi di alto livello hanno costrutti per modelli in linguaggi di livello inferiore che dovrebbero impiegare costanti. Gli stessi pattern possono anche essere usati nella lingua di livello superiore, ma non dovrebbe essere.

Ma quello può essere un giudizio esperto basato sull'impressione di tutte le circostanze e su quale dovrebbe essere la soluzione, e esattamente come quel giudizio sarà giustificato dipenderà pesantemente dal contesto. In effetti, potrebbe non essere giustificabile in termini di qualsiasi principio generale, se non per affermare "Sono abbastanza vecchio per aver già visto questo tipo di lavoro, con il quale mi è familiare, fatto meglio"!

EDIT: avendo accettato una modifica, ne ho rifiutata un'altra e ora ho eseguito la mia modifica personale, posso ora considerare lo stile di formattazione e punteggiatura della mia lista di regole da regolare una volta per tutte haha !

    
risposta data 05.02.2018 - 14:40
fonte
33
  • Sono difficili da tracciare.
  • Cambiare tutto può richiedere la modifica di più file in possibili progetti multipli (difficili da mantenere).
  • A volte è difficile dire quale sia il loro scopo semplicemente osservando il loro valore.
  • Nessun riutilizzo.
risposta data 05.02.2018 - 10:41
fonte
24

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 semplice aggiungere un altro campo, puoi accedervi utilizzando il nome del campo come stringa:

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

(nota la stringa magica "ProductName")

Questo può portare a diversi problemi:

  • Ho bisogno di fare riferimento alla documentazione esterna per sapere che "ProductName" esiste anche e la sua esatta ortografia
  • Inoltre, ho bisogno di fare riferimento a quel documento per vedere qual è il tipo di dati di quel campo.
  • I refusi in questa stringa magica non verranno scoperti fino a quando questa riga di codice non viene eseguita.
  • Quando qualcuno decide di rinominare questo campo sul server (difficile impedendo il dataloss, ma non impossibile), quindi non posso cercare facilmente nel mio codice per vedere dove dovrei modificare questo nome.

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

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

È ancora una costante di stringa e viene compilata esattamente con lo 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 di campo disponibili per questo tipo di entità, anche selezionabili.
  • La documentazione generata automaticamente mostra qual è il significato di questo campo oltre al tipo di dati utilizzato per memorizzare 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)
  • I tipi verranno catturati dal compilatore. Ciò si applica anche quando un nuovo modello (eventualmente dopo aver rinominato o eliminato un campo) viene utilizzato per rigenerare le costanti.

Avanti sulla mia lista: nascondi queste costanti dietro le classi strongmente tipizzate generate, quindi anche il tipo di dati è protetto.

    
risposta data 05.02.2018 - 17:21
fonte
9

Le stringhe di magia non sono sempre male , quindi questo potrebbe essere il motivo per cui non riesci a trovare una ragione generale per evitarli. (Con "stringa magica" presumo tu intenda letterale stringa come parte di un'espressione e non definito come costante.)

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

  • La stessa stringa appare più volte nel codice. Questo significa che potresti avere un errore di ortografia in uno dei posti. E sarà una seccatura delle modifiche alle stringhe. Trasforma la stringa in una costante e eviterai questo problema.
  • La stringa potrebbe cambiare indipendentemente dal codice in cui appare. Per esempio. se la stringa è un testo visualizzato all'utente finale, probabilmente cambierà indipendentemente da eventuali modifiche logiche. Separare tale stringa in un modulo separato (o configurazione esterna o database) renderà più semplice il cambiamento indipendente
  • Il significato della stringa non è ovvio dal contesto. In tal caso l'introduzione di una costante renderà il codice più facile da capire.

Ma in alcuni casi, "stringhe magiche" vanno bene. Supponiamo che tu abbia un parser semplice:

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

Qui non c'è davvero magia e nessuno dei problemi sopra descritti si applica. Non ci sarebbe alcun vantaggio IMHO per definire string Plus="+" ecc. Mantienilo semplice.

    
risposta data 05.02.2018 - 18:15
fonte
5

Per aggiungere risposte esistenti:

Internazionalizzazione (i18n)

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

Alcuni ambienti di sviluppo (ad esempio Qt) gestiscono le traduzioni mediante la ricerca da una stringa di testo in una lingua di base alla lingua tradotta. In genere le stringhe magiche possono sopravvivere fino a quando non decidete di voler usare lo stesso testo altrove e ottenere un errore di battitura. Anche in questo caso, è molto difficile trovare le stringhe magiche che devono essere tradotte quando vuoi aggiungere il supporto per un'altra lingua.

Alcuni ambienti di sviluppo (ad es. MS Visual Studio) adottano un altro approccio e richiedono che tutte le stringhe tradotte siano conservate all'interno di un database di risorse e vengano rilette per le impostazioni internazionali correnti dall'ID univoco di tale stringa. In questo caso, la tua applicazione con stringhe magiche semplicemente non può essere tradotta in un'altra lingua senza grandi rilavorazioni. Uno sviluppo efficiente richiede che tutte le stringhe di testo vengano inserite nel database delle risorse e che venga fornito un ID univoco quando il codice viene scritto per la prima volta, e in seguito i18n è relativamente facile. Cercare di eseguire il backfill dopo il fatto in genere richiederà uno sforzo molto grande (e sì, ci sono stato!) Quindi è molto meglio fare le cose per bene, in primo luogo.

    
risposta data 09.02.2018 - 15:58
fonte
3

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

Ma ancora una volta, questo non è qualcosa a cui gli sviluppatori si interessano.

    
risposta data 06.02.2018 - 16:05
fonte

Leggi altre domande sui tag