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.