Konfigurationsklasse / Struktur: Muster oder Anti-Muster? Alternativen?

10

Wenn Sie einem Programm neue Konfigurationsoptionen hinzufügen, kann dies häufig zu erheblichen Welligkeitseffekten führen, wenn Sie die Optionen dahin bringen, wo sie bearbeitet werden müssen. Es gibt drei grundlegende Möglichkeiten, um damit umzugehen, die mir bekannt sind:

  1. Übergeben Sie alle Konfigurationseinstellungen an die Teile Ihres Programms, die sie explizit als Grundelemente benötigen. Dies ist der expliziteste Weg und der Weg, der die Dinge am meisten entkoppelt. Der Nachteil ist, dass dies sowohl ausführlich als auch spröde ist.

  2. Nehmen Sie die am häufigsten verwendeten Konfigurationseinstellungen global / statisch vor. Dies ist der einfachste Weg, führt jedoch Aktionen aus der Ferne ein, behindert die Testbarkeit und setzt voraus, dass die Konfiguration wirklich global ist (Sie möchten jeweils nur eine Konfiguration).

  3. Erstellen Sie eine Konfigurationsklasse / -struktur, die alle Konfigurationsoptionen für das gesamte Programm oder für jedes Hauptproblem innerhalb des Programms enthält, und geben Sie diese dann explizit weiter. Dies ist weniger explizit als (1), aber expliziter als (2). Wenn Sie eine Einstellung nur für einen Funktionsaufruf ändern möchten, können Sie das Konfigurationsobjekt klonen und diesen einen Wert ändern. Dies ist sowohl beim Testen als auch in der Praxis nützlich. Am Ende übergeben Sie jedoch möglicherweise Tonnen von Informationen an eine Funktion, die nicht benötigt wird, und das Ändern eines Werts in der Konfigurationsklasse / -struktur kann immer noch zu Aktionen in der Ferne führen.

Würden Sie (3) ein Muster oder ein Anti-Muster in Betracht ziehen? Was machen Sie stattdessen, wenn es sich um ein Anti-Pattern handelt?

Dsimcha
quelle
Wie wäre es mit einer Variation von 3 - mit mehreren Konfigurationsklassen, die die entsprechende an die Stelle übergeben, an der sie benötigt wird?
Oded
@Oded: Ich wollte das als eine Möglichkeit hervorheben. Bearbeitet.
Dsimcha

Antworten:

4

Die beste Lösung wäre, mehrere Konfigurationsschnittstellen zu erstellen und diese nach Ihren Wünschen zu implementieren. Dies schränkt sowohl die Zugänglichkeit ein als auch hält die Dinge lokalisiert. Es ist jedoch viel zu viel Aufwand, um es sich zu lohnen, einfach die gesamte Konfiguration in einer einzigen Klasse zu speichern und sich einem Problem mit viel mehr Gravitas zuzuwenden. Dies ist die Konfiguration, nicht die UtterlyCrucialAlwaysChangingClass - sie wird so ziemlich gleich bleiben. Solange Sie nicht alles global gestalten und die Implementierung konsistent ist, würde ich mir darüber keine Sorgen machen.

DeadMG
quelle
4
+1 für die Aussage, dass etwas theoretisch kein ideales Design ist, aber in der Praxis immer noch gut sein kann, wenn Einfachheit und Wahrscheinlichkeit von Änderungen (oder deren Fehlen) berücksichtigt werden.
Dsimcha
Ich habe gerade vier Argumente verworfen und durch eine Settings-Klasse ersetzt. Es fühlte sich wie das Richtige an.
Martin Ueding
Ich verstehe nicht, welche der drei Optionen Sie befürworten. Könnten Sie bitte angeben?
DBedrenko
1

Ich bevorzuge Ihre Option 1, da die Entkopplung ein einfacheres Testen ermöglicht und die Konfigurationseinstellungen, von denen das Objekt abhängt, explizit angegeben werden. Wenn für ein Objekt eine Konfigurationseinstellung erforderlich ist, stellen Sie diese dem Objekt explizit durch ein Konstruktorargument oder eine Setter-Methode zur Verfügung. Reduzieren Sie die Ausführlichkeit, indem Sie ein Abhängigkeitsinjektionsframework verwenden, um diese Konfigurationseinstellungen in das Objekt einzufügen.

Jim Huang
quelle
Sie haben sich selbst widersprochen: Sie sagen, dass Sie Option 1 verwenden sollen, sagen dann aber "Reduzieren Sie die Ausführlichkeit, indem Sie ein Abhängigkeitsinjektionsframework verwenden, um diese Konfigurationseinstellungen in das Objekt einzufügen." Das ist Option 3: Konstruktorinjektion.
DBedrenko
0

Stellen Sie sich vor, Ihre Konfigurationsdatei wurde in XML geschrieben. Sie können dann einfach Fragmente dieses XML an jede Ihrer Komponenten übergeben, damit diese ihre Konfigurationsdaten erhalten.

Wenn Sie .NET verwenden, können Sie mit DataContracts Klassen erstellen, mit denen Sie mit dem XmlSerialiser eine Objekthierarchie aus Ihrer Konfigurations-XML erstellen und diese Objekte als Konfiguration weitergeben können.

Dies führt Sie dann zum nächsten Problem. Ihre Konfigurationsdaten bestehen aus drei verschiedenen Teilen. Die strukturelle Anwendungskonfiguration, die Ihre Codebibliotheken so organisiert, dass sie sich wie dieses spezifische Produkt verhalten. Standortkonfigurationseinstellungen, die Ihre installationsspezifischen Einstellungen und Benutzereinstellungen / Einstellungsdaten enthalten, die von Benutzer zu Benutzer auf Ihrem System variieren.

Wenn Sie wissen, welcher Teil welcher ist, und diese Dateneinstellungen getrennt halten, wird die Installation von Updates viel einfacher (ohne die Kundeneinstellungen zu verlieren).

Michael Shaw
quelle
0

Ich würde die Klasse in Option 3 statisch machen. Also statt

//create SomeCl
Foo f = new Foo();
f.setConfigInst(cfg);
...
...
//Inside Foo
public void setConfig(MyAppConfig c) { localCfg = c; }
...
//somewhere else:
x = localCfg.getConfigForX();

Sie können einfach haben:

//Inside Foo
x = MyAppConfig.getConfigForX();

Lassen Sie die Details zum Laden / Speichern von Konfigurationsdaten innerhalb der MyAppConfigKlasse geschehen . Und natürlich können Sie komplexere Variationen haben, z. B. verschiedene Klassen für verschiedene Zwecke.

Der einzige Fall , in dem dieser Ansatz ein Problem sein würde , aus irgendeinem Grund , wenn Sie an die Arbeit auf mehrere Instanzen von verschiedenen Konfigurationen erforderlich wäre zugleich , obwohl ich noch in einer solchen Situation kommen müssen.

FrustratedWithFormsDesigner
quelle
1
Was genau passiert, wenn Unit-Tests in einigen Test-Frameworks ausgeführt werden ... Aber auch ohne explizit parallele Unit-Tests wird es für Unit-Test-Objekte, die vom globalen Status abhängen, eine Qual sein.
Péter Török
0

Ich arbeite an einem Projekt, bei dem wir den Ansatz "3 Ebenen (Schnittstelle, Geschäftslogik, Datenzugriff)" verwenden. Die Anwendung kann ein Mehrbenutzer-Webserver oder ein Client-Server sein.

Wir arbeiten mit 3 verschiedenen Konfigurationen, die erste spezifisch für den PC, unabhängig davon, ob der Benutzer arbeitet. Die zweite ist spezifisch für den aktuellen Benutzer und eine dritte globale Konfiguration für alle Benutzer und Client-Apps.

Jede Konfiguration wird in jedem Instanz der Anwendung durch ein Objekt dargestellt.

Dieser Ansatz kann Ihrem Projekt helfen.

umlcat
quelle