Dove dovresti mettere le costanti e perché?


34

Nelle nostre applicazioni per lo più grandi, di solito abbiamo solo alcune posizioni per "costanti":

  • Una classe per GUI e costanti interni (titoli delle schede, titoli delle caselle di gruppo, fattori di calcolo, enumerazioni)
  • Una classe per tabelle e colonne di database (questa parte è generata da codice) più nomi leggibili per loro (assegnati manualmente)
  • Una classe per i messaggi dell'applicazione (registrazione, finestre di messaggio ecc.)

Le costanti sono generalmente separate in diverse strutture in quelle classi. Nelle nostre applicazioni C ++, le costanti sono definite solo nel file .h e i valori sono assegnati nel file .cpp.

Uno dei vantaggi è che tutte le stringhe ecc. Sono in un posto centrale e tutti sanno dove trovarle quando qualcosa deve essere cambiato.

Questo è in particolare qualcosa che i project manager sembrano apprezzare quando le persone vanno e vengono e in questo modo tutti possono cambiare cose così banali senza dover scavare nella struttura dell'applicazione.

Inoltre, puoi facilmente cambiare il titolo di simili Caselle di gruppo / Pagine di schede ecc. Contemporaneamente. Un altro aspetto è che puoi semplicemente stampare quella classe e consegnarla a un non programmatore che può verificare se i sottotitoli sono intuitivi e se i messaggi per l'utente sono troppo dettagliati o troppo confusi ecc.

Tuttavia, vedo alcuni svantaggi:

  • Ogni singola classe è strettamente accoppiata alle classi delle costanti
  • L'aggiunta / rimozione / ridenominazione / spostamento di una costante richiede la ricompilazione di almeno il 90% dell'applicazione (Nota: la modifica del valore no, almeno per C ++). In uno dei nostri progetti C ++ con 1500 classi, ciò significa circa 7 minuti di tempo di compilazione (utilizzando intestazioni precompilate; senza di essi è di circa 50 minuti) più circa 10 minuti di collegamento con determinate librerie statiche.
  • La creazione di una versione ottimizzata per la velocità tramite il compilatore di Visual Studio richiede fino a 3 ore. Non so se l'enorme quantità di relazioni di classe sia la fonte, ma potrebbe anche esserlo.
  • Vieni guidato in stringhe temporaneamente hard-coding direttamente nel codice perché vuoi testare qualcosa molto rapidamente e non vuoi aspettare 15 minuti solo per quel test (e probabilmente tutti quelli successivi). Tutti sanno cosa succede al pensiero "Risolverò più tardi".
  • Riutilizzare una classe in un altro progetto non è sempre così facile (principalmente a causa di altri accoppiamenti stretti, ma la gestione delle costanti non lo rende più facile).

Dove vorresti conservare costanti del genere? Inoltre, quali argomenti porteresti per convincere il tuo project manager che esistono concetti migliori che soddisfano anche i vantaggi sopra elencati?

Sentiti libero di dare una risposta C ++ specifica o indipendente.

PS: So che questa domanda è un po 'soggettiva, ma sinceramente non conosco un posto migliore di questo sito per questo tipo di domanda.

Aggiornamento su questo progetto

Ho notizie sulla cosa del tempo di compilazione:
seguendo i post di Caleb e gbjbaanb, quando ho avuto il tempo ho diviso il mio file delle costanti in molti altri file. Alla fine ho anche diviso il mio progetto in diverse librerie che ora era molto più semplice. Compilando questo in modalità di rilascio è emerso che il file generato automaticamente che contiene le definizioni del database (tabella, nomi delle colonne e altro - più di 8000 simboli) e crea alcuni hash ha causato enormi tempi di compilazione in modalità di rilascio.

La disattivazione dell'ottimizzatore di MSVC per la libreria che contiene le costanti DB ora ci ha permesso di ridurre il tempo di compilazione totale del tuo progetto (diverse applicazioni) in modalità di rilascio da un massimo di 8 ore a meno di un'ora!

Dobbiamo ancora scoprire perché MSVC ha difficoltà a ottimizzare questi file, ma per ora questo cambiamento allevia molta pressione poiché non dobbiamo più fare affidamento solo su build notturne.

Questo fatto - e altri benefici, come un accoppiamento meno stretto, una migliore reuseabilità ecc. - hanno anche mostrato che passare del tempo a dividere le "costanti" non era poi una cattiva idea ;-)

Update2

Dato che questa domanda riceve ancora qualche attenzione:
ecco cosa ho fatto negli ultimi anni:

Inserisci ogni costante, variabile, ecc. Esattamente nell'ambito che è pertinente per essa: se usi una costante solo in un singolo metodo, è OK definirla in quel metodo. Se una singola classe è interessata, lasciala come dettaglio di implementazione privato di quella classe. Lo stesso vale per lo spazio dei nomi, il modulo, il progetto, l'ambito aziendale. Uso anche lo stesso modello per le funzioni di supporto e simili. (Questo potrebbe non applicarsi al 100% se si sviluppa un framework pubblico.)

In questo modo aumenta la riusabilità, la testabilità e la manutenibilità in misura tale da non solo dedicare meno tempo alla compilazione (almeno in C ++), ma anche meno tempo alla correzione dei bug, che ti lascia più tempo per sviluppare effettivamente nuove funzionalità. Allo stesso tempo, lo sviluppo di queste funzionalità sarà più veloce poiché puoi riutilizzare più codice più facilmente. Ciò supera di gran lunga qualsiasi vantaggio il file delle costanti centrali potrebbe avere.

Dai un'occhiata in particolare al principio di segregazione dell'interfaccia e al principio di responsabilità singola se vuoi saperne di più.

Se sei d'accordo, vota la risposta di Caleb poiché questo aggiornamento è sostanzialmente una visione più generale di ciò che ha detto.


2
personalmente non avrei affatto titolo dell'interfaccia utente o stringhe di messaggi nelle costanti. Li avrei in app.config
jk.

1
In un certo senso mi piace il modo in cui lo stai facendo ora: capisco i tuoi svantaggi, ma potremmo solo affrontarlo.
bigtang,

1
Ho visto esattamente lo stesso problema in un grande progetto Java ... Un'enorme interfaccia "costanti", cambia qualcosa e aspetta 15 minuti per ricompilare l'eclissi. Sono con Caleb: raggruppa le costanti a cui appartengono naturalmente, vicino al codice che le utilizza. Tenerli separati perché sono costanti è un esercizio OCD inutile.
Michael Borgwardt,

L'accoppiamento stretto non è un problema, perché è quello che vuoi davvero. Si desidera che una modifica nel file delle costanti influisca su molti file di origine. (Naturalmente hai anche altri problemi).
gnasher729,

@ gnasher729 questo è vero solo se molte classi usano la stessa costante. Non vorrai mai che le classi siano strettamente accoppiate a costanti con cui non sono collegati. All'inizio potrebbe non sembrare un problema finché non provi a riutilizzarlo in un altro progetto senza copiarlo o eseguire test isolati
Tim Meyer

Risposte:


29

Le costanti specifiche di una classe dovrebbero andare nell'interfaccia di quella classe.

Le costanti che sono in realtà opzioni di configurazione dovrebbero far parte di una classe di configurazione. Se fornisci gli accessori per le opzioni di configurazione in quella classe (e li usi al posto delle costanti altrove), non dovrai ricompilare il mondo intero quando cambi alcune opzioni.

Le costanti che sono condivise tra le classi ma che non sono pensate per essere configurabili dovrebbero avere un ambito di applicazione ragionevole: prova a suddividerle in file che hanno usi particolari in modo che le singole classi includano solo ciò di cui hanno effettivamente bisogno. Ciò contribuirà nuovamente a ridurre i tempi di compilazione quando si modificano alcune di quelle costanti.


1
Nel caso tu sia interessato: ho aggiornato la mia domanda con quello che ho raggiunto, seguendo la tua risposta e altri
Tim Meyer,

6

Direi semplicemente che vuoi dividere la tua enorme classe di costanti in molti file più piccoli, uno per modulo per esempio. Ciò garantisce che non si abbia una dipendenza così grande dal file delle costanti, quindi l'aggiunta o l'aggiornamento di una stringa non richiederebbe quindi una ricompilazione totale. Puoi comunque archiviare questi file in una posizione centrale, ma (es.) Hanno 1 file con costanti per ogni finestra di dialogo, opportunamente nominato. Quindi è possibile includere tali file solo nei relativi file di dialogo, riducendo in modo massiccio la ricompilazione.

Suggerirei anche di usare qualcosa come le utility GNU GetText per gestire le stringhe, è progettato per la traduzione, ma funziona altrettanto bene semplicemente cambiando il testo in qualcos'altro. Potresti metterli in una risorsa di stringhe, ma trovo che siano più difficili da lavorare in quanto sono codificati da ID, le utilità di GetText sono codificate da una stringa originale - rende le cose molto facili da sviluppare.


Potrei provarci. Dato che la classe delle costanti con titoli, ecc. È divisa in più strutture, potrei forse iniziare una classe per struttura. Non sono sicuro della cosa GNU, di solito non vogliamo cambiare le nostre stringhe in fase di esecuzione, solo durante il tempo di sviluppo. Stiamo usando il meccanismo di traduzione di Qt, nel caso in futuro dovessimo tradurre in un'altra lingua.
Tim Meyer,

Nel caso tu sia interessato: ho aggiornato la mia domanda con quello che ho raggiunto, seguendo la tua risposta e altri
Tim Meyer,

2

Nota: non sono uno sviluppatore C ++ ... ma ecco il mio pensiero: devi considerare di seguire il commento di @ jk sulla differenza tra l'utilizzo dei file di configurazione. In DotNet, ci sono file di risorse che vengono utilizzati per archiviare tali informazioni. In Windows Form, un file di risorse viene gestito da VS per ogni modulo.

Non vedo un valore per una costante da collocare al di fuori del suo ambito di utilizzo a meno che non sia una costante globale che deve essere condivisa. Come hai già detto, questo sarà difficile da mantenere durante lo sviluppo almeno. Inoltre, potresti ricevere scontri di nomi. Un'altra cosa è che potrebbe essere difficile sapere chi sta usando una data costante.

Ora, se si desidera che i non programmatori rivedano le informazioni, per la GUI, si acquisisce la schermata per loro. Se si desidera che rivedano le voci della tabella dei dati, è possibile esportare i dati in Excel o qualcosa di simile.

Se si desidera ancora seguire un approccio di posizione centralizzato e si desidera posizionare tutte le costanti in un unico file di grandi dimensioni, ogni sviluppatore può utilizzare un file condiviso che viene aggiornato alla fine di ogni intervallo in un file centrale. I dati verranno dai singoli file utilizzati nello sviluppo. Questo può essere facilmente automatizzato o fatto manualmente. Tuttavia, come ho detto, probabilmente è un rischio che non devi correre.


2

Non esiste una soluzione generale. Chiediti prestazioni, usabilità, sicurezza e ciclo di vita di una costante.

Più si avvicinano al loro ambito, maggiore è la prestazione.

Più sono raggruppati logicamente e al di fuori del loro ambito, maggiore è la riusabilità.

Meno sono accessibili i costanti, maggiore è la sicurezza.

Maggiore è la durata di una costante, meno si preoccupa di dove la metti per usabilità.

Una costante come un numero di versione verrà definita in qualche tipo di manifest. Un codice di errore di una funzione di errore verrà definito all'interno della classe. Il codice di errore è probabilmente qualcosa con una durata elevata (= non cambia quasi mai). Mettendolo in un file costante, il file viene spam con cose inutili.

Meno la costante ha il carattere di una costante ma di una variabile (come il numero di versione), più la si può mettere all'esterno. Meno variabile è la costante, quindi più costante è, più dovrebbe essere inserita nel suo ambito. Durante il debug ha senso posizionarlo all'esterno per ridurre i tempi di compilazione.

Tuttavia, il problema iniziale è il tempo di compilazione. Quindi la domanda è se si pone la domanda giusta. Se il tempo di compilazione della tua applicazione è troppo alto, dovresti pensare a un modo per renderlo più modulare in modo che le parti lavorino in modo indipendente l'una dall'altra. Compilalo parzialmente e testa i tuoi contenuti in modo indipendente. Se i tuoi test unitari sono stati eseguiti correttamente e sono completi (il che in realtà richiede molto lavoro), puoi facilmente spostare le cose senza dovertene preoccupare. E poi la domanda ha una spinta totalmente diversa.


1

Suggerirei di inserire tutte queste costanti in una sorta di file di configurazione. Per le applicazioni Java, usiamo solitamente i file .properties, un semplice testo con ogni riga formattata come "(chiave) = (valore)". Esempio

MainPanel.Title = Benvenuti nella nostra applicazione
DB.table.users = TBL_USERS
logging.filename = application.log

Quindi caricare questo file in fase di runtime, popolare una cache che consente di cercare una chiave e recuperare un valore. Quando hai bisogno di una costante, esegui una query nella cache. Dovrai comunque avere le tue chiavi da qualche parte e la cache dovrà essere accessibile a livello globale, ma quando modifichi i valori effettivi delle costanti, non è necessario ricompilare, dovrebbe essere possibile semplicemente riavviare l'app (o se vuoi davvero divertirti, avere più file e cache .properties e dare all'applicazione la possibilità di ricaricare una cache in fase di esecuzione).

Per un'implementazione, ho trovato questa domanda SO: https://stackoverflow.com/questions/874052/properties-file-library-for-c-or-c (è stato il primo successo in una ricerca su Google - Non ho ho usato questo software da solo).


Per i progetti C ++, è necessario ricompilare il file delle costanti solo se si modifica un valore. Questo perché i valori sono assegnati in un file .cpp. Tuttavia aggiungere / rimuovere / rinominare / spostare una costante richiede ancora una ricostruzione completa
Tim Meyer

@TimMeyer: se dividi le costanti in più file, l'aggiunta / rimozione di una costante influirebbe solo sui file che dipendono da quel particolare file, giusto?
FrustratedWithFormsDesigner,

Corretta. Il problema principale è che se lo suggerisco, le persone tendono a menzionare una delle cose che ho elencato come "vantaggi" perdersi
Tim Meyer

Tuttavia, in realtà non ho pensato di mettere più file di costanti nello stesso posto
Tim Meyer,

+1. @Tim Meyer: a questo scopo, il controllo in fase di compilazione costa molto più di quanto risparmi. Inoltre, cosa farai se hai bisogno di internazionalizzare?
Kevin Cline,
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.