Ich habe diese alte Implementierung des Befehlsmusters. Es ist eine Art Kontext durch die gesamte DIOperation- Implementierung zu führen, aber ich habe später im Lern- und Lernprozess (der niemals aufhört) festgestellt, dass dies nicht optimal ist. Ich denke auch, dass der "Besuch" hier nicht wirklich passt und nur verwirrt.
Ich denke tatsächlich darüber nach, meinen Code umzugestalten, auch weil ein Befehl nichts über die anderen wissen sollte und im Moment alle dieselben Schlüssel-Wert-Paare haben. Es ist wirklich schwer zu behaupten, welche Klasse welchen Schlüsselwert besitzt, was manchmal zu doppelten Variablen führt.
Ein Anwendungsbeispiel: Angenommen , CommandB benötigt UserName, der von CommandA festgelegt wird . Sollte CommandA den Schlüssel UserNameForCommandB = John setzen ? Oder sollten sie einen gemeinsamen Benutzernamen = John- Schlüsselwert haben? Was ist, wenn der Benutzername von einem dritten Befehl verwendet wird?
Wie kann ich dieses Design verbessern? Vielen Dank!
class DIParameters {
public:
/**
* Parameter setter.
*/
virtual void setParameter(std::string key, std::string value) = 0;
/**
* Parameter getter.
*/
virtual std::string getParameter(std::string key) const = 0;
virtual ~DIParameters() = 0;
};
class DIOperation {
public:
/**
* Visit before performing execution.
*/
virtual void visitBefore(DIParameters& visitee) = 0;
/**
* Perform.
*/
virtual int perform() = 0;
/**
* Visit after performing execution.
*/
virtual void visitAfter(DIParameters& visitee) = 0;
virtual ~DIOperation() = 0;
};
quelle
Antworten:
Ich bin etwas besorgt über die Veränderbarkeit Ihrer Befehlsparameter. Ist es wirklich notwendig, einen Befehl mit sich ständig ändernden Parametern zu erstellen?
Probleme mit Ihrem Ansatz:
Möchten Sie, dass andere Threads / Befehle Ihre Parameter ändern, während
perform
sie ausgeführt werden?Wollen Sie
visitBefore
undvisitAfter
des gleichenCommand
Objekts mit unterschiedlichen aufgerufen werdenDIParameter
Objekte?Möchten Sie, dass jemand Ihren Befehlen Parameter zuführt, von denen die Befehle keine Ahnung haben?
Nichts davon ist durch Ihr aktuelles Design verboten. Während ein generisches Schlüsselwert-Parameterkonzept manchmal seine Vorzüge hat, mag ich es in Bezug auf eine generische Befehlsklasse nicht.
Beispiel für Konsequenzen:
Betrachten Sie eine konkrete Verwirklichung Ihrer
Command
Klasse - so etwas wieCreateUserCommand
. Wenn Sie nun die Erstellung eines neuen Benutzers anfordern, benötigt der Befehl natürlich einen Namen für diesen Benutzer. Welchen Parameter sollte ich setzen, wenn ich dieCreateUserCommand
und dieDIParameters
Klassen kenne ?Ich könnte den
userName
Parameter einstellen oderusername
... behandeln Sie Parameter ohne Berücksichtigung der Groß- und Kleinschreibung? Ich würde es nicht wirklich wissen ... oh warte ... vielleicht ist es nur soname
?Wie Sie sehen können, bedeutet die Freiheit, die Sie durch eine generische Schlüsselwertzuordnung erhalten, dass die Verwendung Ihrer Klassen als jemand, der sie nicht implementiert hat, ungerechtfertigt schwierig ist. Sie müssten zumindest einige Konstanten für Ihre Befehle angeben, damit andere wissen, welche Schlüssel von diesem Befehl unterstützt werden.
Mögliche unterschiedliche Designansätze:
Parameter
Instanzen unveränderlich machen, können Sie sie unter verschiedenen Befehlen frei wiederverwenden.UserParameter
Klasse, die genau die Parameter enthält, die ich für Befehle mit einem Benutzer benötigen würde, wäre die Arbeit mit dieser API viel einfacher. Sie könnten immer noch Vererbung für die Parameter haben, aber es wäre für Befehlsklassen nicht mehr sinnvoll, beliebige Parameter zu verwenden - auf der Pro-Seite bedeutet dies natürlich, dass API-Benutzer genau wissen, welche Parameter erforderlich sind.visitBefore
und enthalten müssenvisitAfter
, während Sie sie gleichzeitig mit verschiedenen Parametern wiederverwenden, sind Sie offen für das Problem, mit unterschiedlichen Parametern aufgerufen zu werden. Wenn die Parameter bei mehreren Methodenaufrufen gleich sein sollen, müssen Sie sie in den Befehl einkapseln, damit sie zwischen den Aufrufen nicht für andere Parameter ausgeschaltet werden können.quelle
Das Schöne an Designprinzipien ist, dass sie früher oder später miteinander in Konflikt stehen.
In der beschriebenen Situation würde ich es vorziehen, mit einem Kontext zu arbeiten, aus dem jeder Befehl Informationen entnehmen und in den er Informationen einfügen kann (insbesondere wenn es sich um Schlüssel-Wert-Paare handelt). Dies basiert auf einem Kompromiss: Ich möchte nicht, dass separate Befehle gekoppelt werden, nur weil sie eine Art Eingabe zueinander sind. In CommandB ist es mir egal, wie UserName eingestellt wurde - nur, dass es für mich da ist. Das Gleiche in CommandA: Ich setze die Informationen ein, ich möchte nicht wissen, was andere damit machen - noch wer sie sind.
Dies bedeutet eine Art vorübergehenden Kontext, den Sie schlecht finden können. Für mich ist die Alternative schlechter, insbesondere wenn dieser einfache Schlüsselwertkontext (kann eine einfache Bohne mit Gettern und Setzern sein, um den "Freiform" -Faktor ein wenig einzuschränken) es ermöglichen kann, dass die Lösung einfach und testbar ist getrennte Befehle, jeder mit seiner eigenen Geschäftslogik.
quelle
Nehmen wir an, Sie haben eine Befehlsschnittstelle:
Und ein Thema:
Was Sie brauchen ist:
Stellen Sie
NameObserver& o
als Verweis auf CommandB. Wenn CommandA jetzt den Betreffnamen ändert, kann CommandB mit den richtigen Informationen ausgeführt werden. Wenn der Name von einem weiteren Befehl verwendet wird, verwenden Sie astd::list<NameObserver>
quelle
Ich weiß nicht, ob dies der richtige Weg ist, um mit Programmierern umzugehen (in diesem Fall entschuldige ich mich), aber nachdem ich alle Antworten hier überprüft habe (insbesondere @ Franks). Ich habe meinen Code folgendermaßen überarbeitet:
Danke für die Hilfe und ich hoffe ich habe hier keine Fehler gemacht :)
quelle