Ich bin ein ziemlicher Pragmatiker, aber mein Hauptanliegen ist, dass Sie möglicherweise zulassen, dass dies ConfigBlock
Ihre Schnittstellendesigns auf möglicherweise schlechte Weise dominiert. Wenn Sie so etwas haben:
explicit MyGreatClass(const ConfigBlock& config);
... eine passendere Schnittstelle könnte so aussehen:
MyGreatClass(int foo, float bar, const string& baz);
... im Gegensatz dazu, diese foo/bar/baz
Felder einfach nur aus einer Masse herauszusuchen ConfigBlock
.
Lazy Interface Design
Auf der positiven Seite macht es diese Art von Design einfach, eine stabile Schnittstelle für Ihren Konstruktor zu entwerfen, z. B. wenn Sie etwas Neues benötigen, können Sie dies einfach in eine laden ConfigBlock
(möglicherweise ohne Codeänderungen) und dann die Wählen Sie die neuen Elemente aus, die Sie benötigen, ohne dass sich die Benutzeroberfläche ändert, sondern nur die Implementierung von MyGreatClass
.
Es ist also sowohl ein Pro als auch ein Contra, dass Sie keine sorgfältig durchdachte Benutzeroberfläche entwickeln müssen, die nur die tatsächlich benötigten Eingaben akzeptiert. Es wendet die Denkweise an: "Gib mir nur diesen riesigen Datenblock, ich werde herausfinden, was ich brauche" und nicht etwa: "Diese genauen Parameter sind das, was diese Schnittstelle benötigt, um zu funktionieren."
Es gibt also definitiv einige Vorteile, die jedoch durch die Nachteile stark aufgewogen werden könnten.
Kupplung
In diesem Szenario haben alle Klassen, die aus einer ConfigBlock
Instanz erstellt werden, folgende Abhängigkeiten:
Dies kann beispielsweise zu einem PITA werden, wenn Sie Class2
in diesem Diagramm einen Einzeltest durchführen möchten . Möglicherweise müssen Sie verschiedene ConfigBlock
Eingaben, die die relevanten Felder enthalten , oberflächlich simulieren Class2
, um sie unter verschiedenen Bedingungen testen zu können.
In jedem neuen Kontext (ob Komponententest oder ganz neues Projekt) können solche Klassen zu einer größeren Belastung für die (Wiederver-) Verwendung werden, da wir sie immer ConfigBlock
für die Fahrt mitnehmen und einrichten müssen entsprechend.
Wiederverwendbarkeit / Bereitstellbarkeit / Testbarkeit
Wenn Sie diese Schnittstellen entsprechend gestalten, können wir sie abkoppeln ConfigBlock
und so etwas erreichen:
Wenn Sie in diesem obigen Diagramm feststellen, werden alle Klassen unabhängig (ihre afferenten / ausgehenden Kopplungen verringern sich um 1).
Dies führt zu viel mehr unabhängigen Klassen (zumindest unabhängig von ConfigBlock
), die in neuen Szenarien / Projekten viel einfacher (wieder) zu verwenden / zu testen sind.
Nun ist dieser Client
Code derjenige, der von allem abhängen und alles zusammensetzen muss. Die Last wird letztendlich auf diesen Clientcode übertragen, um die entsprechenden Felder von a zu lesen ConfigBlock
und sie als Parameter an die entsprechenden Klassen weiterzuleiten. Ein derartiger Client-Code ist jedoch im Allgemeinen eng auf einen bestimmten Kontext zugeschnitten, und die Wiederverwendung kann in der Regel ohnehin nur eingeschränkt oder nur eingeschränkt erfolgen (dies kann beispielsweise die main
Einstiegsfunktion Ihrer Anwendung sein ).
Unter dem Gesichtspunkt der Wiederverwendbarkeit und des Testens kann es daher hilfreich sein, diese Klassen unabhängiger zu machen. Vom Standpunkt der Benutzeroberfläche aus kann es für diejenigen, die Ihre Klassen verwenden, auch hilfreich sein, explizit anzugeben, welche Parameter sie benötigen, anstatt nur einer einzigen Masse, ConfigBlock
die das gesamte Universum der für alles erforderlichen Datenfelder modelliert.
Fazit
Im Allgemeinen neigt diese Art von klassenorientiertem Design, das von einem Monolithen abhängt, der alles Notwendige hat, dazu, diese Art von Eigenschaften aufzuweisen. Ihre Anwendbarkeit, Bereitstellbarkeit, Wiederverwendbarkeit, Testbarkeit usw. können dadurch erheblich beeinträchtigt werden. Sie können jedoch das Schnittstellendesign vereinfachen, wenn wir versuchen, es positiv zu beeinflussen. Es liegt an Ihnen, die Vor- und Nachteile zu messen und zu entscheiden, ob sich die Kompromisse lohnen. In der Regel ist es viel sicherer, sich gegen diese Art von Design zu irren, wenn Sie in Klassen, die im Allgemeinen ein allgemeineres und allgemein anwendbares Design modellieren sollen, aus einem Monolithen heraussuchen.
Zu guter Letzt:
extern CodingBlock MyCodingBlock;
... dies ist potenziell noch schlimmer (mehr schief?) in Bezug auf die oben beschriebenen Merkmale als der Ansatz der Abhängigkeitsinjektion, da Ihre Klassen nicht nur an ConfigBlocks
, sondern direkt an eine bestimmte Instanz davon gekoppelt werden. Dies verschlechtert die Anwendbarkeit / Bereitstellbarkeit / Testbarkeit weiter.
Mein allgemeiner Rat wäre, sich beim Entwerfen von Schnittstellen, die nicht von solchen Monolithen abhängig sind, zu irren, um deren Parameter bereitzustellen, zumindest für die allgemein anwendbaren Klassen, die Sie entwerfen. Und vermeiden Sie den globalen Ansatz ohne Abhängigkeitsinjektion, wenn Sie dies nicht können, es sei denn, Sie haben wirklich einen sehr starken und sicheren Grund, ihn nicht zu vermeiden.
switch
Anweisung oder eineif
Anweisung bezieht, die anhand eines aus den Konfigurationsdateien gelesenen Werts getestet wird.Ja. Es ist besser, Laufzeitkonstanten und -werte sowie den Code zum Lesen zu zentralisieren.
Das ist schlecht: Die meisten Konstruktoren werden die meisten Werte nicht benötigen. Erstellen Sie stattdessen Schnittstellen für alles, was nicht einfach zu konstruieren ist:
alter Code (Ihr Vorschlag):
neuer Code:
Instanziiere eine MyGreatClass:
Hier
current_config_block
ist eine Instanz IhrerConfigBlock
Klasse (die, die alle Ihre Werte enthält) und dieMyGreatClass
Klasse erhält eineGreatClassData
Instanz. Mit anderen Worten, übergeben Sie nur die benötigten Daten an Konstruktoren und fügen Sie FunktionenConfigBlock
zum Erstellen dieser Daten hinzu.Dieser Code deutet darauf hin, dass Sie eine globale CodingBlock-Instanz haben. Tun Sie das nicht: Normalerweise sollten Sie eine Instanz global deklarieren lassen, unabhängig davon, welchen Einstiegspunkt Ihre Anwendung verwendet (Hauptfunktion, DllMain usw.), und diese als Argument übergeben, wo immer Sie dies benötigen (aber wie oben erläutert, sollten Sie sie nicht übergeben die gesamte Klasse herum, nur Schnittstellen um die Daten verfügbar machen und diese übergeben).
Binden Sie Ihre Client-Klassen (Ihre
MyGreatClass
) auch nicht an die Art vonCodingBlock
; Wenn Sie alsoMyGreatClass
eine Zeichenfolge und fünf Ganzzahlen eingeben, ist es besser, diese Zeichenfolge und Ganzzahlen einzugeben, als aCodingBlock
.quelle
Kurze Antwort:
Sie benötigen nicht alle Einstellungen für die einzelnen Module / Klassen in Ihrem Code. Wenn Sie dies tun, stimmt etwas nicht mit Ihrem objektorientierten Design. Insbesondere im Falle von Unit-Tests würde das Setzen aller Variablen, die Sie nicht benötigen, und das Übergeben dieses Objekts beim Lesen oder Verwalten nicht helfen.
quelle
ConfigBlock
Klasse ist. Hier geht es darum, in diesem Fall nicht alle für den Systemstatus erforderlichen Werte anzugeben, sondern nur bestimmte.