Ich muss Kompromisse eingehen: DRY oder Command-Query-Separation?


10

Ich habe kürzlich eine Methode überarbeitet, die sowohl ein Befehl als auch eine Abfragemethode war.

Nachdem ich es in eine Ein-Befehl-Methode und eine Abfragemethode unterteilt hatte, stellte ich fest, dass es jetzt mehrere Stellen im Code gibt, an denen ich den Befehl aufrufe und dann den Wert aus der Abfrage erhalte, was wie eine Verletzung des DRY-Prinzips erscheint.

Aber wenn ich diesen allgemeinen Code in eine Methode einbinden würde, wäre diese Methode sowohl ein Befehl als auch eine Abfrage. Ist das akzeptabel?


Okay, ich wusste nicht, ob die Community sich einig war, und ich konnte keine Diskussion über dieses Thema finden.
Kris Welsh

Es heißt häufiger CQRS google.com.au/…
Daniel Little

@ DanielLittle - nein, ist es nicht. CQS und CQRS sind deutlich unterschiedliche Themen. CQRS ist ein viel komplexeres Architekturmuster, während CQS eher ein Entwurfsmuster ist und viel einfacher zu erfassen und zu implementieren ist. Siehe codebetter.com/gregyoung/2009/08/13/command-query-separation
Erik Funkenbusch

@Erik Funkenbusch Sie haben Recht
Daniel Little

Antworten:


11

Es gibt immer Kompromisse zwischen widersprüchlichen Gestaltungsprinzipien. Der Weg, dies zu lösen, besteht darin, die zugrunde liegenden Gründe für die Prinzipien zu untersuchen. In diesem Fall ist es problematisch, eine Abfrage nicht ausführen zu können, ohne den Befehl auszuführen, aber es ist im Allgemeinen harmlos, einen Befehl nicht ausführen zu können, ohne die Abfrage auszuführen. Solange es eine Möglichkeit gibt, die Abfrage eigenständig auszuführen, sehe ich keinen Grund, das Abfrageergebnis nicht zum Befehl hinzuzufügen, insbesondere wenn Folgendes ausgeführt wird:

QueryResult command()
{
   // do command stuff
   return query();
}

4

Ich habe noch nie von Command-Query-Separation (CQS) gehört, aber es scheint, dass es sich um das Single Responsibility Principle (SRP) handelt, das besagt, dass eine Funktion / Klasse idealerweise für eine Sache und nur eine Sache verantwortlich sein sollte .

Wenn Ihr Befehlscode aus 20 Codezeilen besteht und der Abfragecode aus weiteren 30 Zeilen besteht und sich alle in einem Funktionskörper befinden, verstoßen Sie eindeutig gegen SRP, und ich würde auch von CQS ausgehen, und diese beiden Logikteile sollten voneinander getrennt werden .

Anhand Ihres hypothetischen Beispiels würde ich jedoch höchstwahrscheinlich eine Wrapper-Methode erstellen, die Ihren Befehl und Ihre Abfrage kombiniert, damit DRY an zahlreichen Stellen im Code nicht verletzt wird. Ich würde dies auch nicht als SRP- (und möglicherweise CQS-) Verletzung betrachten, da der Wrapper immer noch nur eine Verantwortung hat: Befehl mit einer Abfrage zu kombinieren und eine Abstraktion auf höherer Ebene zu erstellen, die einfacher zu konsumieren ist.

Ich denke, die Wrapper-Methode ist eine absolut akzeptable Lösung. Um dies zu veranschaulichen, gehen wir noch einen Schritt weiter. Was wäre, wenn Sie 2 Abfragen anstelle von 1 ausführen und dann eine darauf basierende Befehlsaktion ausführen müssten? Ihre 2 Codezeilen wären also 6 oder 8. Was wäre, wenn zwischen einer und der anderen eine Datenüberprüfung / -prüfung durchgeführt würde? Jetzt haben Sie also 15 Codezeilen. Würden Sie zweimal darüber nachdenken, einen Wrapper zu erstellen, der all das erledigt, anstatt diese 15 Zeilen in mehrere Dateien zu streuen?


Ich denke, das "einzige Prinzip" eines Wrappers sollte darin bestehen, die anderen Methoden, die den Befehl und die Abfrage benötigen, zusammen zu halten.
Droogans


Während Karls Lösung für dieses Problem besser ist, finde ich Ihre Ausarbeitung längerer Wrapper-Funktionen ein sehr guter Punkt.
Kris Welsh

-3

DRY ist wichtiger, da es ein viel grundlegenderes Bedürfnis löst - redundante, effektiv verschwendete Anstrengungen zu vermeiden. Dies ist eine grundlegende Sache - man muss kein Programmierer sein, um es zu verstehen.

CQS ist eine Antwort auf die Schwierigkeit, in Sprachen ohne Unterstützung für Tracking-Effekte Code zu verstehen, der sowohl für seine Ergebnisse als auch für seine Auswirkungen ausgeführt wird. Jedoch:

  1. Die Notwendigkeit, Code für seine Ergebnisse auszuführen, kann nicht vermieden werden, da dies die Grundlage für das Zusammenstellen großer Programme aus kleinen Einheiten ist.

  2. Die Notwendigkeit, Code für seine Auswirkungen auszuführen, kann ebenfalls nicht vermieden werden, da der Wert der Ausführung eines Programms außerhalb der Mathematik und der theoretischen Informatik darin liegt, was es beobachtbar für uns tun kann.

  3. Die Notwendigkeit, Effekte zu verursachen und Ergebnisse im selben Code zu erzielen, kann nicht vermieden werden, da wir in der Praxis sowohl Effekte als auch Komposition benötigen, nicht nur den einen oder anderen.

Die eigentliche Lösung für das Problem, dass Tracking-Effekte für Menschen ohne Hilfe zu schwierig sind, besteht natürlich darin, dass Computer uns Menschen helfen ! Ähnliches gilt für die Verfolgung komplexer Beziehungen zwischen Laufzeitwerten (z. B. die Gültigkeit von Array-Indizes), für die Ausnahmen und durch Laufzeit erzwungene Verträge (Nicht-) Lösungen darstellen.

Zusammenfassend lässt sich sagen, dass "Lösungen" wie CQS lediglich die Gestaltung von Programmen nach soliden Prinzipien, die auf der Realität basieren, behindern. Gehen Sie für trocken.


Manchmal müssen Sie die Kopplung vermeiden, um die Komplexität zu verringern. Sie sollten sich CQRS ansehen.
Daniel Little

@Lavinski: Das beste Werkzeug, um Komplexität zu vermeiden (nicht zu verringern, das ist zwecklos), ist die Abstraktion - die Entkopplung des generischen Wesens der Probleme, die wir lösen, von den besonderen Details der Instanzen dieser generischen Probleme. Magische Rezepte (oder "Designmuster", wie ich sie höre) können bestenfalls verhindern, dass Sie zu viel Schaden anrichten, wenn Sie Ihr Design falsch verstehen, aber sie können ein falsches Design nicht in das richtige verwandeln.
Pyon

@Lavinski: In Bezug auf CQRS ist die konzeptionell korrekte alternative Lösung 1. das Datenmodell zu verstehen (keine Anzahl von Objektebenen kann die Notwendigkeit dafür beseitigen), 2. so viele Korrektheitseigenschaften wie möglich im Datenbankschema zu codieren. (Leider bieten die meisten populären RDBMS nur eine begrenzte Unterstützung für letztere, ganz zu schweigen von NoSQL, die dies noch falscher machen. Meine aktuelle Forschung bietet eine bessere Lösung dafür.)
pyon

CQRS arbeitet vollständig im Einklang mit Domain Driven Design. Ich schlage vor, Sie recherchieren ein wenig. Die Domäne in der Anwendung sollte die Richtigkeit erzwingen, nicht Ihr Datenspeicher.
Daniel Little

1
@ EduardoLeón: Wenn Sie beweisen möchten, dass Ihr Design korrekt ist, versuchen Sie, Tests für Ihr Programm zu schreiben. Ich kann Ihnen garantieren, dass das Wegwerfen von CQS Ihre Bemühungen nur behindert.
Stefan Billiet
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.