Frage

Sobald ich eine Diskussion über Design im Verhältnis zum Befehlsmuster hatte. Mein Peer erklärte, dass ein Befehlsobjekt den Status (erfolgreich, erfolglos und warum) nicht zurückgeben sollte, nachdem die Methode .execute () aufgerufen wurde. Der Grund ist, dass Sie sich nicht befassen sollten, wenn der Befehl ausgeführt wird oder nicht, da der Befehl keinen Zustand enthalten darf. Sie müssen jedoch nach dem Aufruf prüfen, ob der Befehl den erwarteten Effekt hatte. Ein weiterer Punkt war, dass das Befehlsmuster in der Bande von vier vier Jahren diesen Fall nicht (der Rückgabestatus) darstellt.

Ich beanspruchte den gegenteiligen Punkt. Der GOF präsentiert diesen Fall nicht, aber ein Muster kann Ihren Anforderungen modelliert werden. Wenn ein Befehl nicht erfolgreich ist, muss der aufgerufene Client einen Nachweis des Status erhalten und schließlich eine angemessene Reaktion einsetzen. Indem der Client gezwungen wurde, zu überprüfen, ob die Aktion erfolgreich erfolgte, war er fehleranfällig und doppelter Code. Darüber hinaus gibt es Fälle, in denen der Befehl ein Ergebnis erzeugt (z. B. ein Befehl, der einem Grundstück eine Zeile hinzufügt "Fish Out" die neue Objektkennung aus dem Datenmodell.

Am Ende haben wir einen Kompromiss erreicht, indem wir den Status nicht zurückgeben, sondern die IDs der neu erstellten Objekte im Befehlsobjekt behalten, und die Anwendung hat sowieso ziemlich gut funktioniert, aber ich bin jetzt neugierig, auch Ihre Meinung zu kennen.

War es hilfreich?

Lösung

In der Frage mit mehreren Antworten gibt es zwei Fragen :) Die erste Frage ist, wenn ein Befehl einen Fehlerstatus zurückgeben sollte?

Jedes Mal, wenn Sie das Muster anwenden, müssen Sie keine klare Antwort für jedes Programm haben, das Sie erneut darüber nachdenken müssen.

Eines der Dinge, über die Sie denken müssen, ist:

  • Füge ich viele Befehle und die Clients für nur bestimmte Fehlerfälle mehr Kopplung hinzu?

Im schlimmsten Fall haben Sie viele Befehle, die sich nicht um Fehler kümmern, aber ein oder zwei Befehle tun etwas, das für den Kunden wichtig ist, um zu wissen, ob es funktioniert hat. Sie fügen jetzt überprüfte Ausnahmen zur Schnittstelle hinzu. Daher müssen jeder Client und jeder Befehl die Fehlerbehandlung durchführen und an die Ausnahme gekoppelt sind. Wenn Sie einen Kunden haben, der sich nur mit Befehlen befasst, die die Ausnahmen nicht veranstalten, haben Sie einen großen Overhead in Ihrem Code.

Dies ist eine Sache, die Sie nicht haben möchten. Sie können also entweder die Befehle verschieben, die Fehler aus der Befehlsstruktur herausziehen müssen, da sie sich von den anderen Befehlen unterscheiden, oder wenn Ihre Sprache es zulässt, können Sie Laufzeitausnahmen hinzufügen, die nur von den Kunden behandelt und von erledigt werden können Die Befehle, die sie werfen müssen.

Das andere Extrem ist, dass jeder Befehl fehlschlagen kann und der Client eine konsistente Art und Weise hat, die Fehler zu behandeln. Dies bedeutet, dass die Fehler nicht vom spezifischen Befehl abhängen. Der Client muss nicht wissen, welche Art von Befehl fehlgeschlagen ist, er kann jeden Fehler auf die gleiche Weise behandeln. Jetzt können Sie die Schnittstelle des Befehls zurückgeben, und der Client kann sich mit den Fehlern befassen. Der Umgang mit den Fehlern sollte jedoch nicht von der Art des Befehls für den Client abhängen.

Die zweite Frage ist: Sollte ein Befehl einen Zustand haben?

Es gibt Architekturen, bei denen ein Befehl einen Zustand benötigt und einige, in denen sie keinen Staat brauchen.

Einige Möglichkeiten, dies zu entscheiden:

  • Wenn Sie einen Rückgänger für Ihren Befehl haben möchten, müssen die Befehle einen Zustand haben.
  • Wenn die Befehle nur zum Verstecken einer Funktion verwendet werden, die auf einem kleinen Satz von Parametern funktioniert, und die Ergebnisse nur vom Befehl wie das Statusmuster abhängt, ist kein Zustand erforderlich, und Sie können dasselbe Objekt über verwenden und über.

  • Wenn Sie den Befehl verwenden, um zwischen Threads zu kommunizieren und Daten von einem Thread auf einen anderen übertragen, benötigt der Befehl einen Status.

  • ... Wenn es etwas gibt, von dem Sie denken, dass diese Liste einen Kommentar hinterlassen sollte.

Andere Tipps

Ich habe keine Designmuster: Elemente wiederverwendbarer objektorientierter Software vor mir im Moment, aber ich bin mir ziemlich sicher, dass die Autoren sogar sagen, dass die von ihnen vorgestellten Designmuster ein Modell sind, das so modifiziert werden kann, dass sie zu einem bestimmten Anpassen sind Lage.

Diese Frage schneidet den Kern dessen, was ein Designmuster ist - eine Vorlage. Es ist nicht etwas, das durch das Buch implementiert werden muss. Sie haben einen Fall identifiziert, in dem eine logische Änderung des Musters, wie im Buch enthalten, der Anwendung geholfen hätte, und das ist vollkommen in Ordnung, insbesondere wenn Sie die Vorteile und Kosten abwägen.

Ich werde mich auf "Head First Design Musters" beziehen. Die Beispiele, die sie für das Befehlsmuster verwenden, sind:

  1. Das Diner -Szenario (Kundin erstellt die Bestellung, das Kellner beruft sie durch das Küchenpersonal, und das Küchenpersonal erhält die Bestellung).
  2. Das Fernsteuerungsszenario (Person klickt auf eine Schaltfläche, Remote Control ruft den Befehl auf und das Gerät empfängt ihn).

Offensichtlich wird im ersten Fall eine Art Staat vom Empfänger hergestellt: "Hier ist das Grub" oder "Wir sind aus Roggenbrot". In einem schicken Restaurant können Sie dies durch Ausnahmebehandlung auf einem höheren Niveau tun (Maitre D 'kommt an den Tisch und entschuldigt sich, bietet einen Ersatz an und kompensiert Ihr Dessert), und das Kellner muss nur die Befehle ordnungsgemäß berufen. Aber bei einem Diner ersetzt der Koch vielleicht voran und ersetzt Brotbrot - das Kellner (und der Kunde) muss in der Lage sein, das zu bewältigen, ohne auf die Theke zu starren: "Wo ist mein Thunfisch auf Roggen?" Dies wird nicht direkt vom Buch behandelt, aber ich denke, es ist eindeutig ein gültiger Fall.

Aber im zweiten Szenario wird der Invoker absichtlich dumm gemacht. Es wird keinen Fehler bei Ihnen aufblitzen, wenn etwas nicht stimmt. Es wird nur keine Wirkung haben. Alle Smarts sind im Kunden, um festzustellen, ob sein Befehl rechtzeitig erfolgreich war ("Mist, ich habe vergessen, es anzuschließen") oder im Empfänger, um herauszufinden, was zu tun ist ("CD: Close CD -Tablett spielen Erste").

Ich bin kein Experte, aber ich würde sagen, dass der Rückkehrstatus an den Invoker für einige Anwendungen völlig in Ordnung ist.

Dies ist definitiv fraglich, aber es zeigt deutlich die beiden Denkstile:

  • Überprüfen Sie, ob etwas in Ordnung ist, und fahren Sie dann entsprechend fort
  • Fahren Sie trotzdem fort und beschäftigen sich damit, wenn etwas Schlimmes passiert

Ich denke nicht, dass einer Weg besser ist als der andere. Zum Beispiel in Java ist es in der Regel am besten, Ihre Ausnahmebehandlung nicht zu missbrauchen und sich um mögliche Probleme zu kümmern, bevor Sie einfach Ihre Hände (und Ausnahmen) in die Luft werfen. Bei Python ist es vorzuziehen, einfach weiter zu versuchen, alles zu tun, unabhängig vom Statuscode, und eine Ausnahme einfach entsprechend behandeln zu lassen.

Es liegt wirklich an Ihnen, ob Sie möchten, dass das Befehlsmuster einen Status zurückgibt oder nicht.

Könnte das Problem hier sein, dass der Befehl von einer Ausführungsklasse ausgeführt wird, die keine direkten Kenntnis darüber gibt, was der Befehl tatsächlich tut.

Wenn wir über das Hinzufügen eines Rückgabetyps zur Ausführungsmethode sprechen, gibt es a Potenzial Um die Implementierungsspezifische Rückgabetypen an den Testamentsvollstrecker vorzustellen. Damit meine ich, dass Sie eine Tür für eine Situation öffnen, in der verschiedene Befehle unterschiedliche Rückgaberwerte haben. Wenn der Testamentsvollstrecker dies wäre, um diese zu handhaben, würde er eng mit den Befehlsimplementierungen verbunden.

Ich habe jedoch häufig den Befehlszustand gegeben - so dass sie durch den Client beim Bau mit Arbeitswerten konfiguriert werden und dann den Gettern ermöglichen, den Client die Ergebnisse der Befehlsausführung nach Abschluss zu extrahieren. In diesem Fall habe ich das Befehlsmuster vielleicht nicht streng befolgt - aber das Design hat gut funktioniert - und es sei denn, es gibt ein definitiver Code -Geruch - ist dies wirklich ein Problem?

Notiz: Trotzdem würde ich interessiert sein, Gedanken darüber zu hören, warum dies ein Codegeruch sein kann.

Sehr schöne Diskussion. Ich habe stundenlang in dieser philosophischen Frage gestellt und bin zu einer Lösung gekommen, die meine Besessenheit erfüllt. (Der Grund, warum ich dieses Zeug liebe, ist, dass es konkrete und abstrakte Logik kombiniert - Booleans + Designs.)

Ich habe kurz darüber nachgedacht, Ausnahmen zur Rückgabe der Ergebnisse zu verwenden. Ich habe diese Idee aufgegeben, weil sie in vielen Fällen die Entkopplung beseitigen würde, das Herz des Musters selbst, wie einige von Ihnen bemerkt haben. Darüber hinaus ist das Ergebnis häufig keine Ausnahme, sondern ein Standard -Rückgabewert. Ich würde wahrscheinlich Geschwüre bekommen.

Letztendlich schrieb ich einem Kunden, der einen Empfänger mit sich selbst instanziiert und die gesamte Logik im Empfänger hielt, wo er hingehört. Der Client ruft nur die Execute () des Befehls an und geht weiter. Der Empfänger kann dann öffentliche Methoden auf dem Kunden aufrufen. Es gibt nichts, was man zurückkehren kann.

Hier ist ein Beispielcode. Ich habe die Befehlsklasse nicht geschrieben, weil ich denke, dass Sie die Idee ohne sie bekommen werden. Die Methode execute () ruft die Run () -Methode des Empfängers auf.

Der Kunde:

Class ClientType{

    CommandType m_Command;
    ReceiverType m_Receiver;
    boolean m_bResult;

    ClientType(){

      m_Receiver = new ReceiverType(this);
      m_Command = new CommandType(m_Receiver);
    }

    public void run(){  
            ... 
      m_Command.execute();
    }


    /*  Decoupled from both the   
     *  command and the receiver. 
     *  It's just a public function that
     *  can be called from anywhere. /
    public setResult(boolean bResult){
      m_bResult = bResult;
    }
}

Der Empfänger:

Class ReceiverType{

    ClientType m_Client;
    boolean m_bResult;

    ReceiverType(ClientType client){
      m_Client = client;
    }

    public void run(){
            ...
      m_Client.setResult(m_bResult);    
    }
}

Auf den ersten Blick könnte es so aussehen, als hätte ich gegen die Entkopplungsanforderung verstoßen. Bedenken Sie jedoch, dass der Kunde nichts über die Implementierung des Empfängers weiß. Die Tatsache, dass der Empfänger weiß, dass er öffentliche Methoden auf dem Kunden anruft, ist Standard -Tarif. Empfänger wissen immer, was sie mit ihren Parameterobjekten tun sollen. Es gibt keine Abhängigkeiten. Die Tatsache, dass der Konstruktor des Empfängers einen ClientTyp -Parameter nimmt, ist irrelevant. Es könnte genauso gut jedes Objekt sein.

Ich weiß, dass dies ein alter Thread ist, hoffe aber, dass einige von Ihnen wieder einbringen werden. Fühlen Sie sich frei, mir das Herz zu brechen, wenn Sie einen Fehler sehen. Das ist ein bisschen, was wir tun.

Wie in Ihrer Frage gesagt:

Wenn ein Befehl nicht erfolgreich ist, muss der aufgerufene Client einen Nachweis des Status erhalten und schließlich eine angemessene Reaktion einsetzen.

In diesem Fall werfe ich Laufzeitausnahmen als Status, der die erforderlichen Informationen dazu enthält. Sie könnten es versuchen.

Grüße,

Ein weiterer Kompromiss besteht darin, eine Eigenschaft "Ausnahmehandler" auf einem konkreten Befehl aufzusetzen, der fehlschlagen kann. Auf diese Weise kann der Schöpfer des Befehls die Ausnahme verarbeiten und Ihrem Client keinen Code -Overhead hinzufügen. Dies ist sehr nützlich, wenn die meisten Ihrer Befehle nicht scheitern sollten.

In meiner CAD/CAM -Software verweisen auf die Befehle, die die Befehle enthalten, auf die Baugruppen, die die Schnittstellen und die Objekthierarchie enthalten, die die verschiedenen UI -Elemente meiner Software enthält. Es ist ähnlich wie a Passive Ansicht

Die Befehle können die Benutzeroberfläche durch die Ansichtsschnittstellen manipulieren und alle Fehler selbst melden.

Grundsätzlich geht es

Formulare implementieren iForminterfaces und registrieren sich mit ScreenViews im EXE

ScreenObjects implementieren ISCreenView und registrieren

Befehlsbaugruppen verweisen auf die ScreenView -Baugruppe und das Modell

ScreenView -Assemblierung kaum mehr als eine Sammlung von Sichtschnittstellen und hält die Anwendungsimplementierung.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top