È mai una buona idea codificare i valori nelle nostre applicazioni? O è sempre la cosa giusta chiamare dinamicamente questi tipi di valori nel caso in cui debbano cambiare?
pipotrebbe cambiare ...
È mai una buona idea codificare i valori nelle nostre applicazioni? O è sempre la cosa giusta chiamare dinamicamente questi tipi di valori nel caso in cui debbano cambiare?
pipotrebbe cambiare ...
Risposte:
Sì, ma rendilo ovvio .
Fare:
Non:
diameter = 2 * radiuso diameter = RADIUS_TO_DIAMETER_FACTOR * radius? Esistono davvero casi angolari in cui un numero magico può essere la soluzione migliore.
diameter = radius << 1? Suppongo che potrebbe anche essere diameter = radius << RADIUS_TO_DIAMETER_BITS_TO_SHIFT.
diameter = radius.toDiameter()
Ciò che trovo strano su questo Q&A finora è che nessuno ha effettivamente tentato di definire chiaramente "hard-code" o, soprattutto, le alternative.
tl; dr: Sì, a volte è una buona idea codificare i valori, ma non esiste una regola semplice su quando ; dipende completamente dal contesto.
La domanda lo restringe ai valori , che prendo per indicare i numeri magici , ma la risposta al fatto che siano o meno una buona idea è relativa a ciò per cui sono effettivamente utilizzati!
Diversi esempi di valori "hardcoded" sono:
Valori di configurazione
Mi arrabbio ogni volta che vedo dichiarazioni come command.Timeout = 600. Perché 600 Chi l'ha deciso? Era già scaduto il tempo prima e qualcuno ha sollevato il timeout come hack invece di risolvere il problema di prestazioni sottostante? O è in realtà qualche aspettativa nota e documentata per i tempi di elaborazione?
Questi non dovrebbero essere numeri o costanti magici , dovrebbero essere esternalizzati in un file di configurazione o in un database da qualche parte con un nome significativo, perché il loro valore ottimale è determinato in gran parte o interamente dall'ambiente in cui l'applicazione è in esecuzione.
Formule matematiche
Le formule di solito tendono ad essere piuttosto statiche, per cui la natura dei valori costanti all'interno non è particolarmente importante. Il volume di una piramide è (1/3) b * h. Ci interessa da dove viene l'1 o il 3? Non proprio. Un precedente commentatore ha giustamente sottolineato che diameter = radius * 2probabilmente è meglio di diameter = radius * RADIUS_TO_DIAMETER_CONVERSION_FACTOR- ma questa è una falsa dicotomia.
Quello che dovresti fare per questo tipo di scenario è la creazione di una funzione . Non ho bisogno di sapere come ti è venuta la formula, ma devo ancora sapere a cosa serve . Se, invece di una qualsiasi assurdità scritta sopra, scrivo volume = GetVolumeOfPyramid(base, height)all'improvviso, tutto diventa molto più chiaro, ed è perfettamente ok avere numeri magici all'interno della funzione ( return base * height / 3) perché è ovvio che fanno solo parte della formula.
La chiave qui è ovviamente avere funzioni brevi e semplici . Questo non funziona per le funzioni con 10 argomenti e 30 linee di calcolo. Utilizzare la composizione della funzione o le costanti in quel caso.
Dominio / regole aziendali
Questa è sempre l'area grigia perché dipende da quale sia esattamente il valore. Il più delle volte, sono questi particolari numeri magici che sono candidati per trasformarsi in costanti, perché ciò rende il programma più facile da capire senza complicare la logica del programma. Considera il test if Age < 19vs if Age < LegalDrinkingAge.; probabilmente puoi capire cosa sta succedendo senza la costante, ma è più facile con il titolo descrittivo.
Questi possono anche diventare candidati per l'astrazione di funzioni, per esempio function isLegalDrinkingAge(age) { return age >= 19 }. L'unica cosa è che spesso la tua logica aziendale è molto più complicata di così, e potrebbe non avere senso iniziare a scrivere dozzine di funzioni con 20-30 parametri ciascuna. Se non c'è una chiara astrazione basata su oggetti e / o funzioni, il ricorso alle costanti è OK.
L'avvertenza è che, se stai lavorando per il dipartimento delle imposte, diventa davvero, davvero oneroso e onestamente inutile scrivere AttachForm(FORM_CODE_FOR_SINGLE_TAXPAYER_FILING_JOINTLY_FOR_DEPRECIATION_ON_ARMPIT_HAIR). Non lo farai, lo farai AttachForm("B-46")perché ogni singolo sviluppatore che abbia mai lavorato o funzionerà lì saprà che "B-46" è il codice del modulo per un singolo contribuente che presenta bla blah blah - i codici del modulo fanno parte del dominio stesso, non cambiano mai, quindi non sono numeri davvero magici.
Quindi devi usare le costanti con parsimonia nella logica aziendale; in pratica devi capire se quel "numero magico" è in realtà un numero magico o se è un aspetto ben noto del dominio. Se si tratta di un dominio, non lo si codifica a meno che non ci siano davvero buone possibilità che cambi.
Codici di errore e flag di stato
Questi non vanno mai bene per il codice, come Previous action failed due to error code 46può dirti qualsiasi povero bastardo che sia mai stato colpito con la . Se la tua lingua lo supporta, dovresti utilizzare un tipo di enumerazione. Altrimenti, di solito avrai un intero file / modulo pieno di costanti che specificano i valori validi per un particolare tipo di errore.
Non farmi mai vedere return 42in un gestore degli errori, capiche? Niente scuse.
Probabilmente ho lasciato fuori diversi scenari, ma penso che li copra la maggior parte.
Quindi, sì, a volte è pratica accettabile scrivere cose sul codice. Solo non essere pigro al riguardo; dovrebbe essere una decisione consapevole piuttosto che un semplice vecchio codice sciatto.
Esistono vari motivi per assegnare un identificatore a un numero.
Questo ci dà i criteri per i letterali hard-coding. Dovrebbero essere immutabili, non difficili da scrivere, presenti in un solo posto o contesto e con un significato riconoscibile. Non ha senso definire 0 come ARRAY_BEGINNING, ad esempio, o 1 come ARRAY_INCREMENT.
Come aggiunta ad altre risposte. Usa le costanti per le stringhe quando possibile. Certo, non vuoi averlo
const string server_var="server_var";
ma dovresti avere
const string MySelectQuery="select * from mytable;";
(supponendo che tu abbia effettivamente una query in cui vuoi ottenere tutti i risultati da una tabella specifica, sempre)
A parte questo, usa le costanti per qualsiasi numero diverso da 0 (di solito). Se hai bisogno di una maschera di bit di autorizzazione di 255, non utilizzare
const int 8th_bit=255; //or some other obscure naming scheme that equates to 255.
invece usa
const int AllowGlobalRead=255;
Naturalmente, insieme alle costanti, sanno quando usare gli enumeratori. Il caso precedente probabilmente si adatterebbe bene in uno.
typedef enum {init_state=0, parse_state=1, evaluation_state=2, ... }
Dipende da cosa consideri hardcoding. Se provi a evitare qualsiasi cosa hardcoded, finisci nel territorio di softcoding e crei un sistema che solo il creatore può gestire (e questo è l'ultimo hardcode)
Un sacco di cose sono codificate in un contesto ragionevole e funzionano. cioè non c'è motivo tecnico per cui non dovrei essere in grado di cambiare il punto di ingresso di un'applicazione C # (vuoto statico Principale), ma hardcoding che non crea alcun problema per nessun utente (tranne la occasionale domanda SO )
La regola empirica che uso è che tutto ciò che può e cambierà, senza influire sullo stato dell'intero sistema, dovrebbe essere confugurabile.
Quindi, IMHO, è sciocco non codificare le cose che non cambiano mai (pi, costante gravitazionale, una costante in una formula matematica - pensa al volume di una sfera).
Inoltre, è sciocco non codificare cose o processi che avranno un impatto sul tuo sistema che richiederà la programmazione in ogni caso, vale a dire è inutile consentire all'utente di aggiungere campi dinamici a un modulo, se qualsiasi campo aggiunto richiederebbe allo sviluppatore di manutenzione di entra e scrivi qualche sceneggiatura che farà funzionare quella cosa. Inoltre è stupido (e l'ho visto alcune volte in ambienti aziendali) creare alcuni strumenti di configurazione, quindi nulla è hardcoded, tuttavia, solo gli sviluppatori del reparto IT possono usarlo, ed è solo leggermente più facile da usare rispetto a per farlo in Visual Studio.
Quindi, in sostanza, se una cosa deve essere hardcoded è una funzione di due variabili:
È mai una buona idea codificare i valori nelle nostre applicazioni?
I valori hardcode solo se i valori sono specificati nella specifica (su una versione finale della specifica), ad es. La risposta HTTP OK sarà sempre 200(a meno che non cambi nella RFC), quindi, vedrai (in alcuni dei miei codici ) costanti come:
public static final int HTTP_OK = 200;
Altrimenti, memorizzo le costanti nel file delle proprietà.
Il motivo per cui ho specificato le specifiche è che la modifica delle costanti nelle specifiche richiede la gestione delle modifiche, in cui le parti interessate esamineranno la modifica e approveranno / disapproveranno. Non succede mai dall'oggi al domani e richiede mesi / anni per un'approvazione. Non dimenticare che molti sviluppatori usano specifiche (ad es. HTTP), quindi cambiarlo significa rompere milioni di sistemi.
Ho notato che ogni volta che puoi estrarre dati dal tuo codice, migliora ciò che resta. Inizi a notare nuovi refactoring e migliorare intere sezioni del tuo codice.
È solo una buona idea lavorare per l'estrazione di costanti, non considerarla una regola stupida, pensala come un'opportunità per programmare meglio.
Il più grande vantaggio sarebbe il modo in cui potresti trovare costanti simili come l'unica differenza nei gruppi di codice: astrarli in array mi ha aiutato a ridurre alcuni file del 90% delle loro dimensioni e a correggere un bel po 'di copia e incolla bug nel frattempo .
Devo ancora vedere un unico vantaggio nel non estrarre dati.
Di recente ho codificato una funzione MySQL per calcolare correttamente la distanza tra due coppie lat / long. Non puoi semplicemente fare pitagora; le linee della longitudine si avvicinano man mano che la latitudine aumenta verso i poli, quindi è coinvolto un po 'di peluria. Il punto è che ero piuttosto lacerato sul fatto che decodificassi o meno il valore che rappresenta il raggio terrestre in miglia.
Ho finito per farlo, anche se il fatto è che le linee lat / lng sono molto più vicine tra loro, diciamo, sulla luna. E la mia funzione avrebbe drasticamente riportato le distanze tra i punti su Giove. Ho pensato che le probabilità del sito web che sto costruendo con l'entrata in una posizione extraterrestre siano piuttosto ridotte.
Bene dipende se la tua lingua è compilata. Se non è compilato, non è un grosso problema, basta modificare il codice sorgente, anche se sarà leggermente delicato per un non programmatore.
Se stai programmando con un linguaggio compilato, questa non è chiaramente una buona idea, perché se le variabili cambiano, devi ricompilare, il che è una grande perdita di tempo se vuoi regolare questa variabile.
Non è necessario creare alcun cursore o interfaccia per modificare dinamicamente la sua variabile, ma il minimo che puoi fare è un file di testo.
Ad esempio con il mio progetto Ogre, utilizzo sempre la classe ConfigFile per caricare una variabile che ho scritto in un file di configurazione.
Due occasioni in cui le costanti sono (almeno secondo me) OK:
Costanti che non si riferiscono a nient'altro; puoi cambiare quelle costanti quando vuoi senza dover cambiare nient'altro. Esempio: la larghezza predefinita di una colonna della griglia.
Costanti assolutamente immutabili, precise, ovvie, come "numero di giorni alla settimana". days = weeks * 7La sostituzione 7con una costante DAYS_PER_WEEKdifficilmente fornisce alcun valore.
Sono completamente d'accordo con Jonathan ma come tutte le regole ci sono eccezioni ...
"Numero magico nelle specifiche: numero magico nel codice"
Sostanzialmente afferma che qualsiasi numero magico che rimane nelle specifiche dopo ragionevoli tentativi di ottenere un contesto descrittivo per loro dovrebbe essere riflesso come tale nel codice. Se i numeri magici rimangono nel codice, ogni sforzo dovrebbe essere fatto per isolarli e renderli chiaramente collegati al loro punto di origine.
Ho eseguito alcuni contratti di interfaccia in cui è necessario popolare i messaggi con valori mappati dal database. Nella maggior parte dei casi la mappatura è abbastanza semplice e si adatterebbe alle linee guida generali di Jonathan, ma ho riscontrato casi in cui la struttura del messaggio di destinazione era semplicemente terribile. Oltre l'80% dei valori che dovevano essere tramandati nella struttura erano costanti imposte dalle specifiche del sistema distante. questo insieme al fatto che la struttura del messaggio era gigantesca faceva sì che MOLTE di tali costanti dovessero essere popolate. Nella maggior parte dei casi non hanno fornito un significato o una ragione, hanno semplicemente detto "inserisci M qui" o "inserisci 4.10.53.10100.889450.4452 qui". Non ho nemmeno tentato di inserire un commento accanto a tutti, il che avrebbe reso illeggibile il codice risultante.
Detto questo, quando ci pensi ... è praticamente tutto per renderlo ovvio ...
Se stai codificando il valore della costante gravitazionale terrestre, a nessuno importa. Se codifichi l'indirizzo IP del tuo server proxy, hai problemi.
Principalmente no, ma penso che valga la pena notare che avrai più problemi quando inizi a duplicare il valore codificato. Se non lo duplicate (es. Usatelo solo una volta nell'implementazione di una classe), allora non usare una costante potrebbe andare bene.