Wo solltest du Konstanten setzen und warum?


34

In unseren meist großen Anwendungen haben wir normalerweise nur wenige Stellen für "Konstanten":

  • Eine Klasse für GUI und interne Konstanten (Registerkartentitel, Gruppentitel, Berechnungsfaktoren, Aufzählungen)
  • Eine Klasse für Datenbanktabellen und -spalten (dieser Teil ist generierter Code) sowie lesbare Namen für diese (manuell zugewiesen)
  • Eine Klasse für Anwendungsnachrichten (Protokollierung, Nachrichtenboxen usw.)

Die Konstanten sind in diesen Klassen normalerweise in verschiedene Strukturen unterteilt. In unseren C ++ - Anwendungen werden die Konstanten nur in der .h-Datei definiert und die Werte in der .cpp-Datei zugewiesen.

Einer der Vorteile ist, dass sich alle Saiten usw. an einem zentralen Ort befinden und jeder weiß, wo er sie finden kann, wenn etwas geändert werden muss.

Dies scheint Projektmanagern besonders zu gefallen, wenn Leute kommen und gehen, und auf diese Weise kann jeder solche trivialen Dinge ändern, ohne sich in die Struktur der Anwendung einarbeiten zu müssen.

Sie können auch einfach den Titel ähnlicher Gruppenfelder / Registerkarten usw. auf einmal ändern. Ein weiterer Aspekt ist, dass Sie diese Klasse einfach ausdrucken und an einen Nicht-Programmierer weitergeben können, der prüfen kann, ob die Beschriftungen intuitiv sind und ob Nachrichten an den Benutzer zu detailliert oder zu verwirrend sind.

Ich sehe jedoch einige Nachteile:

  • Jede einzelne Klasse ist eng mit den Konstantenklassen verbunden
  • Das Hinzufügen / Entfernen / Umbenennen / Verschieben einer Konstante erfordert eine Neukompilierung von mindestens 90% der Anwendung (Hinweis: Das Ändern des Werts ist nicht erforderlich, zumindest für C ++). In einem unserer C ++ - Projekte mit 1500 Klassen bedeutet dies etwa 7 Minuten Kompilierungszeit (bei Verwendung von vorkompilierten Headern; ohne diese beträgt die Kompilierungszeit etwa 50 Minuten) plus etwa 10 Minuten für das Verknüpfen mit bestimmten statischen Bibliotheken.
  • Das Erstellen einer geschwindigkeitsoptimierten Version mit dem Visual Studio Compiler dauert bis zu 3 Stunden. Ich weiß nicht, ob die große Menge an Klassenbeziehungen die Quelle ist, aber es könnte genauso gut sein.
  • Sie werden in vorübergehend hartcodierte Zeichenfolgen direkt in den Code hineingefahren, weil Sie etwas sehr schnell testen möchten und nicht nur 15 Minuten auf diesen Test warten möchten (und wahrscheinlich auf jeden nachfolgenden). Jeder weiß, was mit den Gedanken "Ich werde das später beheben" passiert.
  • Die Wiederverwendung einer Klasse in einem anderen Projekt ist nicht immer so einfach (hauptsächlich aufgrund anderer enger Kopplungen, aber die Handhabung von Konstanten macht es nicht einfacher.)

Wo würden Sie solche Konstanten speichern? Welche Argumente würden Sie mitbringen, um Ihren Projektleiter davon zu überzeugen, dass es bessere Konzepte gibt, die auch die oben genannten Vorteile erfüllen?

Fühlen Sie sich frei, eine C ++ - spezifische oder unabhängige Antwort zu geben.

PS: Ich weiß, dass diese Frage subjektiv ist, aber ich kenne ehrlich gesagt keinen besseren Ort als diese Seite für diese Art von Frage.

Update zu diesem Projekt

Ich habe Neuigkeiten zur Kompilierungszeit:
Nach den Beiträgen von Caleb und gbjbaanb habe ich meine Konstantendatei in mehrere andere Dateien aufgeteilt, als ich Zeit hatte. Schließlich habe ich mein Projekt auch in mehrere Bibliotheken aufgeteilt, was jetzt viel einfacher möglich war. Das Kompilieren im Release-Modus hat gezeigt, dass die automatisch generierte Datei, die die Datenbankdefinitionen (Tabelle, Spaltennamen und mehr - mehr als 8000 Symbole) enthält und bestimmte Hashes aufbaut, die riesigen Kompilierzeiten im Release-Modus verursacht.

Durch Deaktivieren des MSVC-Optimierers für die Bibliothek, die die DB-Konstanten enthält, konnten wir die Gesamtkompilierungszeit Ihres Projekts (mehrere Anwendungen) im Release-Modus von bis zu 8 Stunden auf weniger als eine Stunde reduzieren!

Wir müssen noch herausfinden, warum es MSVC so schwer fällt, diese Dateien zu optimieren, aber diese Änderung entlastet uns vorerst sehr, da wir uns nicht mehr nur auf nächtliche Builds verlassen müssen.

Diese Tatsache - und andere Vorteile wie weniger enge Kopplung, bessere Wiederverwendbarkeit usw. - haben auch gezeigt, dass das Aufteilen der "Konstanten" keine so schlechte Idee war ;-)

Update2

Da diese Frage noch einige Aufmerksamkeit erhält:
Hier ist, was ich in den letzten Jahren getan habe:

Platzieren Sie jede Konstante, Variable usw. genau in dem Bereich, der für sie relevant ist: Wenn Sie eine Konstante nur in einer einzelnen Methode verwenden, ist es in Ordnung, sie in dieser Methode zu definieren. Wenn eine einzelne Klasse daran interessiert ist, belassen Sie es als privates Implementierungsdetail dieser Klasse. Gleiches gilt für Namensraum, Modul, Projekt, Firmenumfang. Ich verwende das gleiche Muster auch für Hilfsfunktionen und dergleichen. (Dies gilt möglicherweise nicht zu 100%, wenn Sie ein öffentliches Framework entwickeln.)

Auf diese Weise werden Wiederverwendbarkeit, Testbarkeit und Wartbarkeit in einem Maße verbessert, in dem Sie nicht nur weniger Zeit für das Kompilieren (zumindest in C ++), sondern auch weniger Zeit für die Fehlerbehebung aufwenden müssen, sodass Sie mehr Zeit für die eigentliche Entwicklung neuer Funktionen haben. Gleichzeitig wird die Entwicklung dieser Funktionen beschleunigt, da Sie mehr Code leichter wiederverwenden können. Dies überwiegt den Vorteil, den die Datei mit den zentralen Konstanten haben könnte, um ein Vielfaches.

Schauen Sie sich insbesondere das Prinzip der Schnittstellentrennung und das Prinzip der Einzelverantwortung an, wenn Sie mehr wissen möchten.

Wenn Sie damit einverstanden sind, stimmen Sie Calebs Antwort zu, da dieses Update im Grunde eine allgemeinere Sicht dessen ist, was er gesagt hat.


2
Ich persönlich hätte überhaupt keinen UI-Titel oder keine Nachrichtenzeichenfolgen in Konstanten. Ich hätte sie in app.config
jk.

1
Ich mag es, wie du es jetzt machst - ich verstehe deine Nachteile, aber wir müssen uns vielleicht darum kümmern.
Bigtang

1
Ich habe genau das gleiche Problem in einem großen Java-Projekt gesehen ... Eine riesige "Konstanten" -Schnittstelle, ändere irgendetwas daran und warte 15 Minuten, bis Eclipse neu kompiliert ist. Ich bin bei Caleb: Gruppieren Sie die Konstanten dort, wo sie von Natur aus gehören, in der Nähe des Codes, der sie verwendet. Sie getrennt zu halten, weil sie Konstanten sind, ist eine nutzlose OCD-Übung.
Michael Borgwardt

Die dichte Kupplung ist kein Problem, denn genau das wollen Sie eigentlich. Sie möchten, dass sich eine Änderung in Ihrer Konstantendatei auf möglicherweise viele Quelldateien auswirkt. (Natürlich hast du auch andere Probleme).
gnasher729

@ gnasher729 Das ist nur wahr, wenn viele Klassen dieselbe Konstante verwenden. Sie möchten niemals, dass Klassen eng an Konstanten gekoppelt sind, mit denen sie nicht verwandt sind. Es scheint zunächst kein Problem zu sein, bis Sie versuchen, es in einem anderen Projekt
Tim Meyer,

Antworten:


29

Konstanten, die für eine Klasse spezifisch sind, sollten in die Schnittstelle dieser Klasse aufgenommen werden.

Konstanten, die wirklich Konfigurationsoptionen sind, sollten Teil einer Konfigurationsklasse sein. Wenn Sie Zugriffsmethoden für die Konfigurationsoptionen in dieser Klasse bereitstellen (und diese anstelle der Konstanten an anderer Stelle verwenden), müssen Sie nicht die ganze Welt neu kompilieren, wenn Sie einige Optionen ändern.

Konstanten, die von Klassen gemeinsam genutzt werden, aber nicht konfigurierbar sind, sollten einen angemessenen Umfang haben. Versuchen Sie, sie in Dateien zu unterteilen, die bestimmte Verwendungszwecke haben, damit die einzelnen Klassen nur das enthalten, was sie tatsächlich benötigen. Dies wird wieder dazu beitragen, die Kompilierzeiten zu verkürzen, wenn Sie einige dieser Konstanten ändern.


1
Wenn Sie interessiert sind: Ich habe meine Frage mit dem, was ich erreicht habe, aktualisiert, im Anschluss an Ihre Antwort und andere
Tim Meyer

6

Ich würde einfach sagen, dass Sie Ihre riesige Konstantenklasse in viele kleinere Dateien aufteilen möchten, eine pro Formular zum Beispiel. Dies stellt sicher, dass Sie keine so große Abhängigkeit von der Konstantendatei haben, sodass das Hinzufügen oder Aktualisieren einer Zeichenfolge keine vollständige Neukompilierung erfordert. Sie können diese Dateien weiterhin an einem zentralen Ort speichern, haben jedoch (z. B.) 1 Datei mit Konstanten für jeden Dialog, die entsprechend benannt sind. Dann können Sie nur diese Dateien in die entsprechenden Dialogdateien aufnehmen, was die Neukompilierung massiv einschränkt.

Ich würde auch vorschlagen, dass Sie GNU GetText-Dienstprogramme verwenden, um die Zeichenfolgen zu verarbeiten. Es ist für die Übersetzung konzipiert, eignet sich aber auch, um den Text einfach in etwas anderes zu ändern. Sie könnten sie in eine String-Ressource aufnehmen, aber ich finde, dass es schwieriger ist, mit ihnen zu arbeiten, da sie durch IDs verschlüsselt sind. Die GetText-Utils sind durch einen Original-String verschlüsselt - das macht die Entwicklung sehr einfach.


Ich könnte das versuchen. Da die Konstantenklasse mit Titeln usw. in mehrere Strukturen aufgeteilt ist, könnte ich vielleicht eine Klasse pro Struktur als Anfang machen. Wir sind uns der Sache mit GNU nicht sicher, normalerweise möchten wir unsere Strings nicht zur Laufzeit ändern, nur während der Entwicklungszeit. Wir verwenden jedoch den Übersetzungsmechanismus von Qt, falls wir in Zukunft in eine andere Sprache übersetzen müssen.
Tim Meyer

Falls Sie interessiert sind: Ich habe meine Frage mit dem, was ich erreicht habe, aktualisiert, im Anschluss an Ihre Antwort und andere
Tim Meyer

2

Hinweis: Ich bin kein C ++ - Entwickler ... aber hier ist mein Gedanke: Sie müssen den Kommentar von @ jk über den Unterschied zwischen der Verwendung von Konfigurationsdateien befolgen. In DotNet gibt es eine Ressourcendatei, in der solche Informationen gespeichert werden. In Windows Forms wird für jedes Formular eine Ressourcendatei aus VS verwaltet.

Ich sehe keinen Wert für eine Konstante außerhalb ihres Verwendungsbereichs, es sei denn, es ist eine globale Konstante, die gemeinsam genutzt werden muss. Wie Sie bereits erwähnt haben, wird es schwierig sein, dies zumindest während der Entwicklung aufrechtzuerhalten. Außerdem kann es zu Namenskonflikten kommen. Eine andere Sache ist, dass es schwierig sein könnte zu wissen, wer eine gegebene Konstante verwendet.

Wenn Sie möchten, dass Nicht-Programmierer Informationen überprüfen, erfassen Sie für die GUI den Bildschirm für sie. Wenn Sie möchten, dass sie Datentabelleneinträge überprüfen, können Sie die Daten in Excel oder ähnliches exportieren.

Wenn Sie dennoch einen zentralen Speicherort festlegen und alle Ihre Konstanten in einer großen Datei speichern möchten, verwendet jeder Entwickler möglicherweise eine gemeinsam genutzte Datei, die am Ende jedes Intervalls in eine zentrale Datei aktualisiert wird. Die Daten stammen aus einzelnen Dateien, die in der Entwicklung verwendet werden. Dies kann einfach automatisiert oder manuell erfolgen. Wie gesagt, es ist wahrscheinlich ein Risiko, das Sie nicht eingehen müssen.


2

Es gibt keine allgemeine Lösung. Fragen Sie sich nach Leistung, Benutzerfreundlichkeit, Sicherheit und Lebenszyklus einer Konstanten.

Je näher sie an ihrem Anwendungsbereich definiert sind, desto höher ist die Leistung.

Je mehr sie logisch gruppiert sind und außerhalb ihres Bereichs liegen, desto höher ist die Wiederverwendbarkeit.

Je weniger zugänglich die Kunden sind, desto höher ist die Sicherheit.

Je höher die Lebensdauer einer Konstante ist, desto weniger interessiert es, wo Sie sie für die Verwendbarkeit ablegen.

Eine Konstante wie eine Versionsnummer wird in einer Art Manifest definiert. Ein Fehlercode einer Fehlerfunktion wird innerhalb der Klasse definiert. Der Fehlercode hat wahrscheinlich eine hohe Lebensdauer (= ändert sich fast nie). Wenn Sie es in eine konstante Datei einfügen, wird die Datei nur mit unnötigem Material überflutet.

Je weniger die Konstante den Charakter einer Konstante hat, aber eine Variable (wie die Versionsnummer), desto mehr können Sie sie nach außen verschieben. Je weniger variabel die Konstante ist, je konstanter sie ist, desto mehr sollte sie innerhalb ihres Gültigkeitsbereichs platziert werden. Während des Debuggens ist es sinnvoll, es außerhalb zu platzieren, um die Kompilierungszeit zu verkürzen.

Ihr erstes Problem ist jedoch die Kompilierungszeit. Die Frage ist also, ob Sie die richtige Frage stellen. Wenn die Kompilierungszeit Ihrer Anwendung zu hoch ist, sollten Sie besser überlegen, wie Sie sie modularer gestalten können, damit die Teile unabhängig voneinander funktionieren. Kompiliere es teilweise und teste deine Sachen unabhängig voneinander. Wenn Ihre Komponententests ordnungsgemäß durchgeführt wurden und vollständig sind (was tatsächlich eine Menge Arbeit bedeutet), können Sie problemlos Dinge verschieben, ohne sich darum kümmern zu müssen. Und dann bekommt die Frage einen ganz anderen Antrieb.


1

Ich würde vorschlagen, alle diese Konstanten in eine Art Konfigurationsdatei zu schreiben. Für Java-Anwendungen verwenden wir normalerweise .properties-Dateien, einen einfachen Text, wobei jede Zeile als "(key) = (value)" formatiert ist. Beispiel

MainPanel.Title = Willkommen zu unserer Anwendung
DB.table.users = TBL_USERS
logging.filename = application.log

Anschließend laden Sie diese Datei zur Laufzeit, füllen einen Cache, mit dem Sie einen Schlüssel nachschlagen und einen Wert zurückerhalten können. Wenn Sie eine Konstante benötigen, fragen Sie den Cache ab. Sie müssen Ihre Schlüssel noch irgendwo haben, und der Cache muss global zugänglich sein. Wenn Sie jedoch die tatsächlichen Werte der Konstanten ändern , ist keine Neukompilierung erforderlich. Sie sollten die App nur neu starten können (oder wenn Sie möchten wirklich ausgefallen sein, über mehrere .properties-Dateien und -Caches verfügen und der Anwendung die Möglichkeit geben, einen Cache zur Laufzeit neu zu laden.

Für eine Implementierung habe ich diese SO-Frage gefunden: https://stackoverflow.com/questions/874052/properties-file-library-for-c-or-c (es war der erste Treffer bei einer Google-Suche - habe ich nicht habe diese Software tatsächlich selbst benutzt).


Für die C ++ - Projekte müssen wir die Konstantendatei nur neu kompilieren, wenn wir einen Wert ändern. Dies liegt daran, dass Werte in einer CPP-Datei zugewiesen werden. Das Hinzufügen / Entfernen / Umbenennen / Verschieben einer Konstante erfordert jedoch noch einen vollständigen Neuaufbau
Tim Meyer,

@ TimMeyer: Wenn Sie die Konstanten in mehrere Dateien aufteilen, wirkt sich das Hinzufügen / Entfernen einer Konstante nur auf die Dateien aus, die von dieser bestimmten Datei abhängen.
FrustratedWithFormsDesigner

Richtig. Das Hauptproblem ist, dass, wenn ich das vorschlage, die Leute dazu neigen, eines der Dinge zu erwähnen, die ich als "Vorteile" aufgelistet habe, die verloren gehen
Tim Meyer

Allerdings habe ich nicht wirklich darüber nachgedacht, mehrere Konstantendateien an der gleichen Stelle
Tim Meyer,

+1. @Tim Meyer: Zu diesem Zweck kostet die Überprüfung der Kompilierungszeit viel mehr als sie spart. Außerdem, was wirst du tun, wenn du internationalisieren musst?
Kevin Cline
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.