Eliminazione dei numeri magici: quando è il momento di dire "No"?

36

Sappiamo tutti che i numeri magici (valori codificati) possono causare il caos nel programma, specialmente quando è il momento di modificare una sezione di codice che non ha commenti, ma dove si disegna la linea?

Ad esempio, se hai una funzione che calcola il numero di secondi tra due giorni, sostituisci

seconds = num_days * 24 * 60 * 60

con

seconds = num_days * HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE

A che punto decidi che è del tutto ovvio cosa significhi il valore hard-coded e lasciarlo da solo?

    
posta oosterwal 09.03.2011 - 17:30
fonte

10 risposte

39

Ci sono due ragioni per usare le costanti simboliche invece dei valori letterali numerici:

  1. Per semplificare la manutenzione se i numeri magici cambiano. Questo non si applica al tuo esempio. È estremamente improbabile che il numero di secondi in un'ora o il numero di ore in un giorno cambierà.

  2. Per migliorare la leggibilità. L'espressione "24 * 60 * 60" è abbastanza ovvia per quasi tutti. Anche "SECONDS_PER_DAY" è troppo, ma se stai inseguendo un bug, potresti dover controllare che SECONDS_PER_DAY sia stato definito correttamente. C'è un valore in brevità.

Per i numeri magici che appaiono esattamente una volta e che sono indipendenti dal resto del programma, decidere se creare un simbolo per quel numero è una questione di gusti. In caso di dubbio, vai avanti e crea un simbolo.

Non farlo:

public static final int THREE = 3;
    
risposta data 09.03.2011 - 18:45
fonte
29

Manterrei la regola di non avere mai numeri magici.

Mentre

seconds = num_days * 24 * 60 * 60

È perfettamente leggibile il più delle volte, dopo aver codificato per 10 ore al giorno per tre o quattro settimane in modalità crunch

seconds = num_days * HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE

è molto più facile da leggere.

Il suggerimento di FrustratedWithFormsDesigner è migliore:

seconds = num_days * DAYS_TO_SECOND_FACTOR

o anche meglio

seconds = CONVERT_DAYS_TO_SECONDS(num_days)

Le cose smettono di essere ovvie quando sei molto stanco. Codice difensivo .

    
risposta data 09.03.2011 - 17:37
fonte
8

Il tempo di dire no è quasi sempre. I tempi in cui trovo che è più semplice usare solo numeri codificati in posti come il layout dell'interfaccia utente - creare una costante per il posizionamento di ogni controllo sul modulo diventa molto cubmersone e faticoso e se tale codice viene solitamente gestito da un progettista dell'interfaccia utente non importa molto. ... a meno che l'interfaccia utente non sia strutturata dinamicamente, o usi posizioni relative a qualche ancora o sia scritta a mano. In tal caso, direi che è meglio definire alcune costanti significative per il layout. E se hai bisogno di un fattore fudge qua o là per allineare / posizionare qualcosa "giusto", dovrebbe anche essere definito.

Ma nel tuo esempio, penso che sostituire 24 * 60 * 60 di DAYS_TO_SECONDS_FACTOR sia migliore.

Concedo che i valori codificati sono anche OK quando il contesto e l'utilizzo sono completamente chiari. Questo, tuttavia, è una sentenza ...

Esempio:

Come sottolineato da @rmx, usando 0 o 1 per verificare se una lista è vuota, o forse nei limiti di un loop è un esempio di un caso in cui lo scopo della costante è molto chiaro.

    
risposta data 09.03.2011 - 17:33
fonte
8

Interrompi quando non riesci a definire un significato o uno scopo per il numero.

seconds = num_days * HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE

è molto più facile da leggere rispetto all'uso dei numeri. (Anche se potrebbe essere reso più leggibile avendo una singola costante di SECONDS_PER_DAY , ma è un problema completamente separato.)

Supponiamo che uno sviluppatore che osserva il codice possa vedere cosa fa. Ma non dare per scontato che sappiano anche perché. Se la tua costante aiuta a capire il perché, fallo. In caso contrario, non.

Se ti ritrovassi con troppe costanti, come suggerito da una sola risposta, considera l'utilizzo di un file di configurazione esterno, dato che le dozzine di costanti in un file non migliorano esattamente la leggibilità.

    
risposta data 09.03.2011 - 19:42
fonte
7

Probabilmente direi "no" a cose come:

#define HTML_END_TAG "</html>"

E sicuramente direbbe "no" a:

#define QUADRATIC_DISCRIMINANT_COEF 4
#define QUADRATIC_DENOMINATOR_COEF  2
    
risposta data 10.03.2011 - 04:21
fonte
7

Uno dei migliori esempi che ho trovato per promuovere l'uso delle costanti per cose ovvie come HOURS_PER_DAY è:

Stavamo calcolando quanto a lungo c'erano le cose nella coda dei lavori di una persona. I requisiti sono stati definiti in modo approssimativo e il programmatore ha codificato con difficoltà 24 in un numero di punti. Alla fine ci siamo resi conto che non era giusto punire gli utenti che si sono seduti su un problema per 24 ore quando in realtà lavorano solo per 8 ore al giorno. Quando l'attività è arrivata a risolvere questo problema E vedere quali altri report potrebbero avere lo stesso problema è stato piuttosto difficile eseguire grep / search nel codice per 24 sarebbe stato molto più facile da grep / search per HOURS_PER_DAY

    
risposta data 10.03.2011 - 17:40
fonte
4

Penso che finché il numero è completamente costante e non ha possibilità di cambiare, è perfettamente accettabile. Quindi nel tuo caso, seconds = num_days * 24 * 60 * 60 va bene (presupponendo ovviamente che tu non faccia qualcosa di sciocco come fare questo tipo di calcolo all'interno di un ciclo) e discutibilmente meglio per la leggibilità rispetto a seconds = num_days * HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE .

È quando fai cose così brutte:

lineOffset += 24; // 24 lines to a page

Anche se non potresti adattare più righe sulla pagina o anche se non hai intenzione di cambiarla, usa invece una variabile costante, perché un giorno tornerà a perseguitarti. In definitiva, il punto è la leggibilità, non il salvataggio di 2 cicli di calcolo sulla CPU. Questo non è più il 1978 quando i preziosi byte sono stati spremuti per tutto il loro valore.

    
risposta data 09.03.2011 - 17:48
fonte
3
seconds = num_days * 24 * 60 * 60

Va perfettamente bene. Questi non sono numeri magici in quanto non cambieranno mai.

Qualsiasi numero che possa ragionevolmente cambiare o non avere un significato ovvio dovrebbe essere inserito in variabili. Il che significa praticamente tutti loro.

    
risposta data 09.03.2011 - 19:59
fonte
3

Eviterei di creare costanti (valori magici) per convertire un valore da un'unità all'altra. In caso di conversione, preferisco un nome di metodo parlante. In questo esempio, questo sarebbe ad es. DayToSeconds(num_days) interno il metodo non ha bisogno di valori magici perché, il significato di "24" e "60" è chiaro.

In questo caso non userei mai secondi / minuti / ore. Userei solo TimeSpan / DateTime.

    
risposta data 21.07.2014 - 17:40
fonte
1

Usa il contesto come parametro per decidere

Ad esempio, hai una funzione chiamata "calculateSecondsBetween: aDay and: anotherDay", non dovrai fare molto adapenazione su cosa facciano quei numeri, perché il nome della funzione è abbastanza rappresentativo.

E un'altra domanda è: quali sono le possibilità di calcolarlo in un modo diverso? A volte ci sono molti modi per fare la stessa cosa, quindi per guidare i futuri programmatori e mostrare loro quale metodo hai usato, definire le costanti potrebbe aiutarti a capirlo.

    
risposta data 09.03.2011 - 17:36
fonte

Leggi altre domande sui tag