Ich denke, diese Frage sollte für die meisten Programme gelten, die Einstellungen aus einer Datei laden. Meine Frage ist aus programmtechnischer Sicht und es geht wirklich darum, wie man mit dem Laden von Einstellungen aus einer Datei in Bezug auf verschiedene Klassen und Zugänglichkeit umgeht. Zum Beispiel:
- Wenn ein Programm eine einfache
settings.ini
Datei hatte, sollte sein Inhalt in eineload()
Methode einer Klasse oder vielleicht in den Konstruktor geladen werden ? - Sollten die Werte in
public static
Variablen gespeichert werden oder solltenstatic
Methoden zum Abrufen und Festlegen von Eigenschaften vorhanden sein? - Was soll passieren, wenn die Datei nicht vorhanden oder nicht lesbar ist? Wie würden Sie den Rest des Programms wissen lassen, dass es diese Eigenschaften nicht erhalten kann?
- etc.
Ich hoffe, ich frage das hier an der richtigen Stelle. Ich wollte die Frage so sprachunabhängig wie möglich machen, aber ich konzentriere mich hauptsächlich auf Sprachen, die Dinge wie Vererbung haben - insbesondere Java und C # .NET.
Antworten:
Dies ist eigentlich eine wirklich wichtige Frage und wird oft falsch gemacht, da ihr nicht genügend Bedeutung beigemessen wird, obwohl sie ein Kernbestandteil so ziemlich jeder Anwendung ist. Hier sind meine Richtlinien:
Ihre Konfigurationsklasse, die alle Einstellungen enthält, sollte nur ein einfacher alter Datentyp sein, struct / class:
Es sollte keine Methoden benötigen und keine Vererbung beinhalten (es sei denn, es ist die einzige Wahl, die Sie in Ihrer Sprache zur Implementierung eines Variantenfelds haben - siehe nächster Absatz). Es kann und sollte Komposition verwenden, um die Einstellungen in kleinere spezifische Konfigurationsklassen zu gruppieren (z. B. subConfig oben). Wenn Sie dies auf diese Weise tun, ist es ideal, Unit-Tests und die Anwendung im Allgemeinen weiterzugeben, da sie nur minimale Abhängigkeiten aufweist.
Sie müssen wahrscheinlich Variantentypen verwenden, falls Konfigurationen für verschiedene Setups heterogen aufgebaut sind. Es wird davon ausgegangen, dass Sie irgendwann eine dynamische Umwandlung vornehmen müssen, wenn Sie den Wert lesen, um ihn in die richtige (Unter-) Konfigurationsklasse umzuwandeln. Dies hängt zweifellos von einer anderen Konfigurationseinstellung ab.
Sie sollten nicht faul sein, alle Einstellungen als Felder einzugeben, indem Sie einfach Folgendes tun:
Dies ist verlockend, da Sie eine verallgemeinerte Serialisierungsklasse schreiben können, die nicht wissen muss, mit welchen Feldern sie sich befasst, aber es ist falsch und ich werde gleich erklären, warum.
Die Serialisierung der Konfiguration erfolgt in einer völlig separaten Klasse. Unabhängig davon, welche API oder Bibliothek Sie dazu verwenden, sollte der Hauptteil Ihrer Serialisierungsfunktion Einträge enthalten, die im Grunde genommen einer Zuordnung vom Pfad / Schlüssel in der Datei zum Feld auf dem Objekt entsprechen. Einige Sprachen bieten eine gute Selbstbeobachtung und können dies sofort für Sie tun, andere müssen Sie das Mapping explizit schreiben, aber das Wichtigste ist, dass Sie das Mapping nur einmal schreiben müssen. Betrachten Sie beispielsweise diesen Auszug, den ich aus der Parser-Dokumentation zu den C ++ - Boost-Programmoptionen angepasst habe:
Beachten Sie, dass in der letzten Zeile grundsätzlich "Optimierung" für Config :: opt steht und dass eine Deklaration des erwarteten Typs vorhanden ist. Sie möchten, dass das Lesen der Konfiguration fehlschlägt, wenn der Typ nicht Ihren Erwartungen entspricht, wenn der Parameter in der Datei nicht wirklich ein float oder ein int ist oder nicht vorhanden ist. Dh Fehler sollte auftreten, wenn Sie die Datei lesen, da das Problem im Format / in der Validierung der Datei liegt und Sie einen Ausnahme- / Rückkehrcode auslösen und das genaue Problem melden sollten. Sie sollten dies nicht zu einem späteren Zeitpunkt im Programm verzögern. Das ist der Grund, warum Sie nicht versucht sein sollten, die oben erwähnte Conf im Dictionary-Stil abzufangen, die beim Lesen der Datei nicht fehlschlägt - da das Casting verzögert wird, bis der Wert benötigt wird.
Sie sollten die Config-Klasse in gewisser Weise schreibgeschützt machen - indem Sie den Inhalt der Klasse beim Erstellen einmal festlegen und aus der Datei initialisieren. Wenn Sie dynamische Einstellungen in Ihrer Anwendung benötigen, die sich ändern, sowie konstante Einstellungen, die dies nicht tun, sollten Sie eine separate Klasse haben, um die dynamischen Einstellungen zu verarbeiten, anstatt zu versuchen, zuzulassen, dass Bits Ihrer Konfigurationsklasse nicht schreibgeschützt sind .
Idealerweise lesen Sie die Datei an einer Stelle in Ihrem Programm ein, dh Sie haben nur eine Instanz eines "
ConfigReader
". Wenn Sie jedoch Schwierigkeiten haben, die Config-Instanz an den gewünschten Ort weiterzuleiten, ist es besser, einen zweiten ConfigReader zu haben, als eine globale Konfiguration einzuführen (was das OP vermutlich unter "statisch" versteht "), was mich zu meinem nächsten Punkt bringt:Vermeiden Sie das verführerische Sirenenlied des Singletons: "Ich erspare Ihnen, dass Sie diese Klassenklasse weitergeben müssen, alle Ihre Konstrukteure werden nett und sauber sein. Weiter, es wird so einfach sein." Die Wahrheit ist, dass Sie mit einer gut gestalteten testbaren Architektur kaum die Config-Klasse oder Teile davon durch so viele Klassen Ihrer Anwendung weitergeben müssen. Was Sie in Ihrer Top-Level-Klasse, Ihrer main () -Funktion oder was auch immer finden, entwirren Sie die conf in einzelne Werte, die Sie Ihren Komponentenklassen als Argumente zur Verfügung stellen, die Sie dann wieder zusammensetzen (manuelle Abhängigkeit) Injektion). Ein singleton / global / static conf erschwert das Implementieren und Verstehen von Unit-Tests Ihrer Anwendung erheblich. Dies verwirrt beispielsweise neue Entwickler in Ihrem Team, die nicht wissen, dass sie den globalen Status zum Testen von Inhalten festlegen müssen.
Wenn Ihre Sprache Eigenschaften unterstützt, sollten Sie diese für diesen Zweck verwenden. Der Grund dafür ist, dass es sehr einfach ist, abgeleitete Konfigurationseinstellungen hinzuzufügen, die von einer oder mehreren anderen Einstellungen abhängen. z.B
Wenn Ihre Sprache die Eigenschaftssprache nicht nativ unterstützt, kann es eine Problemumgehung geben, um den gleichen Effekt zu erzielen, oder Sie erstellen einfach eine Wrapper-Klasse, die die Bonuseinstellungen bereitstellt. Wenn Sie den Vorteil von Eigenschaften nicht anderweitig nutzen können, ist es ansonsten Zeitverschwendung, manuell zu schreiben und Getter / Setter zu verwenden, nur um einem OO-Gott zu gefallen. Mit einem einfachen alten Feld sind Sie besser dran.
Möglicherweise benötigen Sie ein System, um mehrere Konfigurationen von verschiedenen Orten in der Reihenfolge ihrer Priorität zusammenzuführen und zu übernehmen. Diese Rangfolge sollte für alle Entwickler / Benutzer klar definiert und verständlich sein, z. B. die Windows-Registrierung HKEY_CURRENT_USER / HKEY_LOCAL_MACHINE berücksichtigen. Sie sollten diesen funktionalen Stil ausführen, damit Ihre Konfigurationen schreibgeschützt bleiben, dh:
eher, als:
Abschließend möchte ich natürlich hinzufügen, dass Sie, wenn Ihr ausgewähltes Framework / Ihre Sprache eigene integrierte, bekannte Konfigurationsmechanismen bietet, die Vorteile dieser Verwendung in Betracht ziehen sollten, anstatt Ihre eigenen zu rollen.
So. Viele Aspekte, die berücksichtigt werden müssen - machen Sie es richtig und es wird Ihre Anwendungsarchitektur tiefgreifend beeinflussen, Fehler reduzieren, Dinge leicht testbar machen und Sie dazu zwingen, gutes Design an anderer Stelle zu verwenden.
quelle
.ini
damit sie für Menschen lesbar ist. Schlagen Sie jedoch vor, eine Klasse mit den Variablen in zu serialisieren?Im Allgemeinen (meiner Meinung nach) ist es am besten, die Anwendung die Speicherung der Konfiguration behandeln zu lassen und die Konfiguration an Ihre Module zu übergeben. Dies ermöglicht Flexibilität bei , wie die Einstellungen gespeichert werden , so dass Sie Dateien oder Webdiensten oder Datenbanken Ziel oder ...
Außerdem trägt die Anwendung die Last "Was passiert, wenn Dinge fehlschlagen", wer am besten weiß, was dieser Fehler bedeutet.
Und es macht es viel einfacher, Unit-Tests durchzuführen, wenn Sie nur ein Konfigurationsobjekt übergeben können, anstatt das Dateisystem berühren oder Parallelitätsprobleme lösen zu müssen, die durch statischen Zugriff entstehen.
quelle
Wenn Sie .NET zum Programmieren der Klassen verwenden, haben Sie verschiedene Optionen wie Ressourcen, web.config oder sogar eine benutzerdefinierte Datei.
Wenn Sie Resources oder web.config verwenden, werden die Daten tatsächlich in einer XML-Datei gespeichert, das Laden ist jedoch schneller.
Das Abrufen der Daten aus diesen Dateien und das Speichern an einem anderen Speicherort entspricht einer doppelten Verwendung des Speichers, da diese standardmäßig in den Speicher geladen werden.
Für jede andere Datei oder Programmiersprache funktioniert die obige Antwort von Benedict.
quelle