Il contesto (come un argomento in una funzione) consente numeri in codice che non sono numeri magici? [duplicare]

11

Ho letto molti degli articoli e simili sui numeri magici, cioè

  1. articolo di Wikipedia
  2. articolo programmers.stackexchange.com

Sembrava che nessuno avesse considerato il contesto come un mezzo per rendere accettabile l'uso di un numero e non averlo considerato magico.

Ad esempio

connection.setLoginTimeoutSeconds(30);

Vs

final int LOGIN_TIMEOUT_30_SECONDS = 30;
connection.setLoginTimeoutSeconds(LOGIN_TIMEOUT_30_SECONDS);

o anche

final int LOGIN_TIMEOUT = 30;
connection.setLoginTimeoutSeconds(LOGIN_TIMEOUT);

Il nome e il valore della funzione hanno tutte le informazioni come nome e valore della variabile:

setLoginTimeoutSeconds(30);
LOGIN_TIMEOUT_SECONDS = 30;

Nel caso in cui una variabile per il timeout di accesso non sia necessaria in nessun'altra parte del codice, sembra che il primo esempio sia più leggibile e ancora più gestibile rispetto agli ultimi due e che il numero 30 non sia magico a causa di dove risiede. Qualcuno è d'accordo o in disaccordo?

Non sto chiedendo costanti ben conosciute come in are- tutto-magic-numeri-create-the-same . Qualsiasi valore costante può fare. Né sto chiedendo che il mio esempio sia trovato difettoso perché qualcuno potrebbe creare un problema da esso. Quello che sto veramente chiedendo è l'opinione di chiunque su se le regole del numero magico non devono essere applicate.

    
posta superbAfterSemperPhi 08.10.2015 - 17:06
fonte

6 risposte

6
final int LOGIN_TIMEOUT_SECONDS = 30;
// other code
connection.setLoginTimeoutSeconds(LOGIN_TIMEOUT_SECONDS);

Questo sembra risolvere tutti i problemi / dilemmi di cui ti stai chiedendo.

Aiuta anche con alcune verifiche rapide, poiché vedi che stai trasmettendo le unità corrette nel metodo (se fosse setLoginTimeoutMS (LOGIN_TIMEOUT_SECONDS) vedresti chiaramente un errore.

Stavo per scrivere un commento ma voglio invece indirizzare:

It seemed like no one considered has context as a means of making it acceptable to use a number and not have it considered magic.

Cambiamenti di contesto. Non vuoi fare affidamento su qualcosa che potrebbe cambiare.

Diamo un'occhiata al tuo "buon" esempio:

final int LOGIN_TIMEOUT = 30;
connection.setLoginTimeoutSeconds(LOGIN_TIMEOUT);

Il problema con i numeri magici non è sapere cosa significano. In questo esempio la tua denominazione non aiuta veramente e peggiora le cose. 30 secondi in secondi? C'è stato un problema di conversione delle unità?

So che la tua connessione ha un valore in secondi, ma il problema è che la tua costante magica non aiuta a chiarire questo punto. Può essere letto molto facilmente "chiama la funzione di timeout di accesso, che accetta un argomento di secondi e passa in un valore di tempo arbitrario che si spera sia secondi."

Se lo dichiari immediatamente prima del login, chiamalo può essere chiaro. Ma cosa succede quando il codice viene refactored per avere un timeout di accesso comune? O quando viene aggiunto altro codice tra la definizione / l'uso?

Infine, non dipendere dal contesto per aggiungere chiarezza quando puoi apportare modifiche ai nomi molto semplici per aggiungere la stessa chiarezza. Modifiche al contesto. se qualcuno sposta la dichiarazione in un file di intestazione per supportare l'utilizzo comune, il tuo contesto non esiste più. Boom. E proprio così hai reso il tuo sistema meno manutenibile.

Inoltre, ci sono dei momenti in cui una costante può essere utile (singoli usi, ecc.). Ma la domanda che stai ponendo si applica ancora: potresti solo sapere più chiaramente che il contesto non cambierà.

    
risposta data 08.10.2015 - 18:45
fonte
22

Non è solo che il numero stesso è cattivo, ma che possono essere introdotte incomprensioni quando lo scopo non è chiarito e c'è un numero di tali valori. Basandosi sul tuo esempio:

connection.setLoginTimeout(30); 
connection.setSessionTimeout(30);

A meno che tu non abbia familiarità con il codice, non è chiaro se entrambi i valori dovrebbero essere uguali o se possono essere diversi. Quindi se dovessero essere uguali allora:

const TIMEOUT = 30;

connection.setLoginTimeout(TIMEOUT); 
connection.setSessionTimeout(TIMEOUT);

O se potevano essere valori diversi allora:

const LOG_TIMEOUT = 30;
const SESS_TIMEOUT = 30;

connection.setLoginTimeout(LOG_TIMEOUT); 
connection.setSessionTimeout(SESS_TIMEOUT);

Potresti obiettare che questo non si applica a un'impostazione, ma se ne dovrebbe aggiungere un'altra in un secondo momento con lo stesso valore iniziale, l'incomprensione che ho delineato sopra potrebbe facilmente verificarsi.

    
risposta data 08.10.2015 - 17:30
fonte
10

Il primo grosso problema con

connection.setLoginTimeout(30);

È che stai assumendo che lo sviluppatore junior che hai appena assunto e che guarda da 6 mesi saprà che "30" è secondi e amp; non millisecondi.

Il secondo grosso problema (come sottolineato da @KilianFoth in un commento) è che ancora, tra 6 mesi, lo sviluppatore junior potrebbe non essere in grado di trovare questo parametro molto facilmente, se non del tutto.

Un punto importante non presente nei tuoi esempi o negli esempi nell'altra domanda è che non c'è assolutamente alcun motivo per dichiarare la costante proprio accanto a dove è stata usata.

Metti invece tutti questi "parametri di ottimizzazione" in un luogo ben documentato comune, forse un file Constants.h o equivalente.

    
risposta data 08.10.2015 - 17:31
fonte
3

setLoginTimeoutSeconds(30) indica che 30 è (ora) il timeout.

Non fornisce spiegazioni sul motivo per cui impostiamo il timeout su 30.
È perché il timeout del server è 20 e vogliamo ulteriori 10 secondi per i ritardi della rete? È lo stesso con il timeout di logout definito nella riga successiva? È qualcosa che è stato deciso dal test di usabilità e si trova in un file di configurazione? O forse non ha significato ed è stato scelto come predefinito in modo casuale (qualcosa che è ancora buono sapere)?

Sì, potrebbe essere che non c'è più nulla da dire su questo numero, in questo caso nominarlo TIMEOUT è ugualmente privo di significato chiamandolo THIRTY (e TIMEOUT_IN_SECONDS non è particolarmente utile, questo dovrebbe essere idealmente dedotto dal contesto ). Ma in tal caso, perché abbiamo bisogno di impostarlo affatto? Potrebbe essere più sensato avere il connection di lavoro senza doverlo impostare su 30.

In altre parole, se non c'è niente al di fuori del contesto da dire, forse non dovrebbe esserci un numero.

    
risposta data 08.10.2015 - 18:44
fonte
1

The primary purpose of the DATA statement is to give names to constants; instead of referring to pi as 3.141592653589793 at every appearance, the variable PI can be given that value with a DATA statement and used instead of the longer form of the constant. This also simplifies modifying the program, should the value of pi change.

FORTRAN di base Xerox e manuale FORTRAN IV di base , attribuito a David H. Owens. Fonte: link

Ovviamente, i valori costanti comunemente utilizzati dovrebbero essere utilizzati tramite dichiarazioni costanti che rendono evidente a chiunque stia osservando il codice quale sia il valore, e per rendere più semplice la modifica del valore in futuro, se necessario . Tuttavia, questo non deve essere visto come un dettame assolutamente vincolante che dovrebbe essere seguito con devozione dogmatica.

Considera la seguente implementazione di una funzione hashCode() :

/**
 * Computes the FNV hash code of all elements in this unmodifiable enumerable.
 */
static <E> int hashCode( UnmodifiableEnumerable<E> self )
{
    int hash = 17;
    for( E element : self )
        hash = 31 * hash + Objects.hashCode( element );
    return hash;
}

Come dice il commento, questa è la (più comune versione semplificata della) standard Funzione hash Fowler-Noll-Vo che la maggior parte dei programmatori conosce. Non ha senso creare costanti per il 17 e il 31, poiché ciò complicherebbe ulteriormente le cose. Sono solo due numeri primi, il loro uso è altamente localizzato, (solo all'interno di questa funzione,) è altamente improbabile che cambi mai, e se c'è mai la necessità di cambiarli, la persona che deciderà di cambiarli meglio sa molto bene quello che stanno facendo, e quindi meglio conoscere molto bene ciò che rappresentano.

Altre volte, esistono numeri che sono così permanentemente espressi nella pietra, e quindi molto ben conosciuti da tutti, che a volte le costanti non sono necessarie. L'esilarante citazione del Basic FORTRAN IV Manual lo illustra bene, ma ecco un altro esempio: una settimana ha sette giorni in tutto il mondo, e questo è altamente improbabile che cambi mai, quindi un'espressione semplice che coinvolge identificatori esistenti che hanno già i nomi significativi non hanno nulla a cui trarre beneficio sostituendo un valore letterale 7 con una costante. Chi ha davvero bisogno di vedere days = weeks * DAYS_PER_WEEK invece di days = weeks * 7 ?

(Naturalmente, se l'espressione è più complicata, o se gli altri identificatori che prendono parte all'espressione non hanno tali nomi che si spiegano da soli, potrebbe essere ancora meglio usare DAYS_PER_WEEK piuttosto che 7 , allo scopo di documentare ciò che stai facendo.)

Detto questo, devi stare attento ai problemi culturali.

Alla pagina 300 del libro "Clean Code" di Prentice Hall, l'autore Robert C. Martin dice:

And in the FEET_PER_MILE case, the number 5280 is so very well known and so unique a constant that readers would recognize it even if it stood alone on a page with no context surrounding it.

A cui devo dire (estratto dal mio blog )

The world is not the USA. Most of the world uses the metric system, and knows very little about miles and feet. When given a number in "United States customary units", many people outside the USA know how to convert it to metric, but it is rare to meet someone who is proficient in converting from metric to US units, and even more rare to meet someone who has the slightest clue, or the slightest interest, in converting between US units. Therefore, the number 5280 means absolutely nothing to the vast majority of the population of the planet. As a matter of fact, there are so many non-USAians working as software engineers in the USA, that even in the USA it would be a bad idea to assume that anyone who sees the number 5280 in a piece of code will know what it stands for.

Infine, i numeri magici nel loro vero senso (di avere un significato segreto) dovrebbero essere quasi sempre dichiarati con una costante, altrimenti la loro presenza nel mezzo del codice sorgente può essere molto sconcertante, ma anche questa regola ha un'eccezione. Se utilizzerai solo un particolare numero magico una volta nella tua intera base di codice, e se deve apparire in un pezzo di codice come il seguente, allora va bene, perché ciò che sta accadendo è molto ovvio:

if( fileObject.getMagicNumber() == 0xbadbeef ) { ... }
    
risposta data 08.10.2015 - 22:14
fonte
0

Un altro punto non ancora menzionato è che ci sono volte in cui un valore finisce per essere codificato all'interno della logica di un programma oltre ad apparire come un letterale. Ad esempio, una funzione per calcolare la media di tre consecutivi i valori in un array possono essere scritti in modo più efficiente e pulito come

Average = (arr [i-1] + arr [i] + arr [i + 1]) / 3.0; // presume arr sia double []

che usare un ciclo (fare cinque senza un loop spingerebbe le cose, e sette sarebbero probabilmente più puliti con un ciclo, ma per tre cose il codice in linea è più semplice). Il valore letterale 3.0 ripete semanticamente il numero di termini aggiunti, che è una leggera violazione di "Do not Repeat Yourself", ma dal momento che entrambi gli usi sono vicini, sarà piuttosto ovvio come si relazionano. Rendere la 3.0 una costante nominata renderebbe molto meno chiaro che la modifica della costante non causerebbe il calcolo di un numero maggiore di termini con una media corretta, ma farebbe semplicemente in modo che il calcolo restituisca il valore sbagliato.

    
risposta data 08.10.2015 - 23:44
fonte

Leggi altre domande sui tag