Wie viel Logik kann in einen Befehl eingegeben werden? Oder anders: Für welche Art von Logik ist das Befehlsmuster?


8

Ich benutze das Befehlsmuster schon seit einiger Zeit, bin mir aber nie sicher, wie viel Logik ich tatsächlich in die ExecuteMethode einbauen kann .

Meine aktuelle Implementierung des Befehlsmusters sieht folgendermaßen aus:

public abstract class Command
{
    public static event EventHandler Completed = delegate { };        

    public bool Success { get; private set; }

    public Exception Exception {get; private set; }

    public abstract bool Execute();

    protected bool OnCompleted(bool success, Exception ex = null)
    {       
        Success = success;
        Exception = ex;
        Completed(this, EventArgs.Empty)
        return success;
    }
}

und das sind die Fragen, die ich mir stelle (und in meinen Befehlen übe):

  1. Ist es in Ordnung, Nachrichtenfelder anzuzeigen oder Dialoge zum Öffnen von Dateien usw. anzuzeigen?
  2. Ist es in Ordnung, Eigenschaften eines Objekts festzulegen?
  3. Kann ein Befehl Geschäftslogik enthalten?
  4. Kann ein Befehl die GUI-Steuerelemente trotzdem ändern?
  5. Zu welchen Ebenenbefehlen gehören? Ansicht oder Datenschicht? Kann ich Befehle in beiden Ebenen haben?
  6. Kann ein Befehl all das tun, was vorher in der war button1_Click?
  7. Sollte ein Befehl von Unit-Testable?
  8. Kann ein Befehl als zu sehen Benutzer , dass Marken die APIs verwenden und baut die letzte Schicht einer Anwendung und auch die letzte Mittel Ausnahmen zu fangen?
  9. Können Befehle auch per Code ausgeführt werden (Befehl ruft API auf, API wird ausgeführt und schließlich ruft eine andere API einen Befehl auf) oder kann nur der Benutzer ihn aufrufen und die APIs dürfen nichts über ihre Existenz wissen?
  10. Gibt es einen Platz für Befehle in MVC oder MVVC oder einem anderen Entwurfsmuster mit einer Steuerung? Sie scheinen sich gegenseitig auszuschließen. Was ist vorzuziehen?

Es gibt viele Tutorials, die zeigen, wie das Befehlsmuster implementiert wird, aber keines beschreibt tatsächlich, wie es in einer echten Wold-Anwendung angewendet wird.

Antworten:


7

Das Befehlsmuster wird im Allgemeinen verwendet, um WAS von WER und WAS von WANN zu entkoppeln. Dies ist der Vorteil einer einfachen Benutzeroberfläche, die so einfach ist wie:

public abstract class Command {
    public abstract void execute();
}

Stellen wir uns vor, Sie haben eine Klasse EngineOnCommand. Sie können diesen Befehl an andere Objekte übergeben, die Befehlsinstanzen akzeptieren. Dies bedeutet, dass die Klasse, die diesen EngineOnCommand empfängt, auch einen anderen Befehl erhalten kann, der von ihr ausgeführt werden soll, und dass sie ihn niemals erfahren sollte. Dies bedeutet, dass Sie WAS von der WHO entkoppelt haben .

Nun ist der zweite Fall für den Befehl Patterm abzukoppeln WAS von WHEN . Stellen Sie sich vor, Sie möchten ein System erstellen, in dem die Aktionen in der Datenbank nur nachts ausgeführt werden sollen, jedoch in der Reihenfolge, in der sie angefordert wurden. Mit einer Warteschlange von Befehlen, in der Sie einen nach dem anderen ausführen können, hätten Sie dies erreichen können. Die Idee ist, dass der Aufruf des Befehls tatsächlich nur eine Warteschlange des Befehls in einer Liste auslöst, die später ausgeführt werden soll.

Ich hoffe, meine obigen Beispiele helfen zu verstehen, was die Idee des Musters ist. Jetzt werde ich versuchen, einige Ihrer Fragen auf der Grundlage der obigen Ausführungen zu beantworten.

Kann ein Befehl Geschäftslogik enthalten?

Ja, ich glaube, es sollte es enthalten. Es wird es entkoppelt von WER und WANN enthalten.

Kann ein Befehl die GUI-Steuerelemente trotzdem ändern?

Dies bedeutet, dass Sie es an die GUI koppeln. Dies bedeutet, dass es nicht dazu verwendet wird, WAS von der WHO zu entkoppeln. Der Befehl sollte im Idealfall nicht wissen, wer ihn aufruft, ob es sich um eine GUI oder eine Batch-Funktionalität handelt.

Sollte ein Befehl von Unit-Testable?

Es sollte nicht, es muss einheitlich testbar sein . Der gesamte Code sollte Unit-testbar sein, aber im Idealfall müssen alle Befehle Unit-testbar sein.

Es tut mir leid, dass Sie nicht alle Ihre Fragen beantwortet haben, aber ich glaube, Sie sollten das GOF- Buch lesen . Es enthält einige gute Beispiele.


+1 Hervorragende Antwort, wenn auch nur ein kleiner Trottel: Es ist großartig, sich für eine bestimmte Praxis wie Unit-Tests einzusetzen, aber diese Praktiken so zu beherrschen , als ob sie allgemein vorgeschrieben wären, ist für junge Programmierer in der Regel nicht hilfreich. Aus Erfahrung sprechen.
Phil

2

Der größte Vorteil von Befehlen besteht darin, dass sie es einfach machen, eine Aktion rückgängig zu machen, eine Aktion zu wiederholen, eine Aktion an mehreren Stellen (über eine Netzwerkverbindung) auszuführen oder sie zu einem späteren Zeitpunkt auszuführen usw.

In diesem Sinne:

  1. Wahrscheinlich nicht. Es ist schwer vorstellbar, dass es sinnvoll ist, ein Dialogfeld "rückgängig zu machen", und so etwas würde ich lieber in den UI-Code einfügen.

  2. Solange auf dieses Objekt von überall aus zugegriffen werden kann, wo der Befehl ausgeführt werden soll, ist es natürlich in Ordnung, seine Eigenschaften zu ändern.

  3. Absolut. Dies hängt davon ab, ob Ihr Modell Geschäftslogik "enthalten" soll. Es wird tatsächlich als Anti-Pattern ("anämisches Domain-Modell") angesehen, um zu wenig Geschäftslogik zu haben.

  4. Wenn die GUI-Steuerelemente Teil Ihres Modells sind, unbedingt. Wenn nicht, ist es fraglich.

  5. Auf keinen Fall die Aussicht. Die Ansicht sollte darüber informiert werden, dass sich die Daten oder das Modell geändert haben, und entsprechend reagieren, aber die tatsächliche Änderung sollte in den Daten oder im Modell stattfinden.

  6. Im Prinzip wahrscheinlich ja. Befehle rückgängig machen zu können, ist eines der besten Dinge an dem Muster.

  7. Die Funktion execute () des Befehls sollte auf jeden Fall einheitlich getestet werden. Wenn Befehle an ein Modell gebunden sind, gehören sie zu den am einfachsten zu testenden Teilen einer Anwendung.

  8. Ich bin mir nicht ganz sicher, was du meinst, aber:

    • Für eine externe API ist es vollkommen in Ordnung, Befehle als Eingabe oder Ausgabe zu verwenden. Vorausgesetzt, Sie validieren sie natürlich.
    • "letzte Schicht" klingt für mich wie eine Ansicht. In MVC sollten die Befehle wahrscheinlich vom Controller generiert und vom Modell verarbeitet werden. Es wäre also sinnvoll zu sagen, dass die Ansicht "über" dem Befehlsmaterial erstellt wird, wenn Sie dies wollten.
    • Ausnahmen wahrscheinlich nicht. In MVC würde ich erwarten, dass der Controller die Ausnahmen abfängt und sie in die richtige Art von Dialogfeld verwandelt. Nicht das Modell / die Befehle.
  9. Absolut. Die meisten Vorteile von Befehlen sind unmöglich zu implementieren, wenn der Code sie nie ausführt.

  10. Es ist wahrscheinlich mittlerweile offensichtlich, aber ich sehe sie überhaupt nicht als sich gegenseitig ausschließend an. In meinem Team bei der Arbeit übersetzt der Controller benutzergenerierte Ereignisse in Befehle, das Modell empfängt und führt die Befehle aus (und speichert "Rückgängig" -Befehle irgendwo), und wenn dies erledigt ist, weist der Controller die Ansicht an, sich basierend auf dem neuen Modell selbst zu aktualisieren . Die Befehle werden auch über das Netzwerk an alle Kopien des Modells gesendet, die von anderen Benutzern angezeigt werden, sodass sie es auch sehen. Es funktioniert hervorragend.

Und die Titelfrage: Wie viel Logik muss in einen Befehl eingegeben werden? Ich würde kein Minimum oder Maximum darauf setzen. Was ich sagen würde, ist, dass es eine sehr gute Idee ist, komplexere Befehle mit einer Reihe einfacherer Befehle zu implementieren und Befehle auf die gleiche Weise zu refaktorieren, wie Sie Funktionen refaktorieren würden.

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.