Verwenden eines Strategiemusters und eines Befehlsmusters


121

Beide Entwurfsmuster kapseln einen Algorithmus und entkoppeln Implementierungsdetails von ihren aufrufenden Klassen. Der einzige Unterschied, den ich erkennen kann, besteht darin, dass das Strategiemuster Parameter für die Ausführung berücksichtigt, während das Befehlsmuster dies nicht tut.

Es scheint mir, dass das Befehlsmuster erfordert, dass alle Informationen für die Ausführung verfügbar sind, wenn es erstellt wird, und dass es den Aufruf verzögern kann (möglicherweise als Teil eines Skripts).

Welche Bestimmungen bestimmen, ob das eine oder das andere Muster verwendet werden soll?

Antworten:


94

Ich füge eine Kapselungshierarchietabelle mit mehreren GoF-Entwurfsmustern hinzu, um die Unterschiede zwischen diesen beiden Mustern zu erläutern. Hoffentlich zeigt es besser, was jeder verkapselt, damit meine Erklärung sinnvoller ist.

Zunächst listet die Hierarchie den Bereich auf, für den ein bestimmtes Muster anwendbar ist, oder das geeignete Muster, mit dem eine bestimmte Detailebene gekapselt werden kann, je nachdem, auf welcher Seite der Tabelle Sie beginnen.

Hierarchie-Tabelle für die Kapselung von Entwurfsmustern

Wie Sie der Tabelle entnehmen können, verbirgt ein Strategiemusterobjekt Details der Implementierung eines Algorithmus, sodass die Verwendung eines anderen Strategieobjekts dieselbe Funktionalität auf unterschiedliche Weise ausführt. Jedes Strategieobjekt kann für einen bestimmten Faktor optimiert sein oder mit einem anderen Parameter arbeiten. Durch die Verwendung einer gemeinsamen Schnittstelle kann der Kontext sicher mit beiden arbeiten.

Das Befehlsmuster kapselt einen viel kleineren Detaillierungsgrad als ein Algorithmus. Es codiert die Details, die zum Senden einer Nachricht an ein Objekt erforderlich sind: Empfänger, Selektor und Argumente. Der Vorteil der Objektivierung eines so kleinen Teils der Prozessausführung besteht darin, dass solche Nachrichten allgemein zu verschiedenen Zeitpunkten oder an verschiedenen Orten aufgerufen werden können, ohne dass ihre Details fest codiert werden müssen. Damit können Nachrichten ein- oder mehrmals aufgerufen oder an verschiedene Teile des Systems oder an mehrere Systeme weitergeleitet werden, ohne dass die Details eines bestimmten Aufrufs vor der Ausführung bekannt sein müssen.

Wie es für Entwurfsmuster typisch ist, müssen nicht alle Implementierungen im Detail identisch sein, um den Musternamen zu tragen. Details können in der Implementierung und in den im Objekt codierten Daten im Vergleich zu Methodenargumenten variieren.


Wenn ich also ein System hätte, das Ergebnisse mit einer "Filterpipeline" filtert und Delegaten als Filter verwendet (wobei jeder der Filteralgorithmen in einer Funktion gekapselt wäre), würde dies als Befehlsmuster betrachtet? In diesem Fall sehe ich den Delegaten für die Filterfunktion als eine Art Vertrag für das, was jeder Filter in Bezug auf Eingabe und Ausgabe einhalten muss.
KTF

4
@KTF, nein. Das Befehlsmuster verwendet ein Objekt, das die meisten (wenn nicht alle) Informationen enthält (z. B. Empfänger, Selektor, Argumente), um die Methode eines Objekts aufzurufen. Es ist ein vereinfachtes Muster, das in anderen Entwurfsmustern wie Verantwortungskette, Sammlung und dem von Ihnen beschriebenen Pipeline-Muster verwendet werden kann. Der von Ihren Delegierten bereitgestellte "Art Vertrag" ist ein weiteres Muster, "Schnittstelle".
Huperniketes

50

Strategien kapseln Algorithmen. Befehle trennen den Absender vom Empfänger einer Anfrage, sie verwandeln eine Anfrage in ein Objekt.

Wenn es sich um einen Algorithmus handelt, verwenden Sie eine Strategie. Wenn Sie den Aufruf einer Methode von ihrer Ausführung trennen müssen, verwenden Sie einen Befehl. Befehle werden häufig verwendet, wenn Sie Nachrichten für eine spätere Verwendung in die Warteschlange stellen, z. B. eine Aufgabe oder eine Transaktion.


das machte Sinn en.wikipedia.org/wiki/Command_Pattern Client und Aufrufer sind miteinander verbunden, aber gleichzeitig wissen sie nichts voneinander!
Kalpesh Soni

26

Beantwortung einer sehr alten Frage. (sieht jemand die letzten Antworten anstatt die meisten Stimmen?)

Aufgrund der Ähnlichkeiten ist es eine berechtigte Verwirrung. Sowohl Strategie- als auch Befehlsmuster verwenden die Kapselung . Aber das macht sie nicht gleich.

Der Hauptunterschied besteht darin, zu verstehen, was gekapselt ist. Das OO-Prinzip, von dem beide Muster abhängen, lautet: Verkapseln, was variiert .

Im Falle einer Strategie variiert der Algorithmus . Beispielsweise kann ein Strategieobjekt in eine XML-Datei ausgeben, während das andere beispielsweise in JSON ausgibt. Verschiedene Algorithmen werden in verschiedenen Klassen aufbewahrt ( gekapselt ). So einfach ist das.

Im Falle eines Befehls variiert die Anforderung selbst. Anfrage kann von File Menu > Deleteoder Right Click > Context Menu > Deleteoder kommenJust Delete Button pressed . Alle drei Fälle können 3 Befehlsobjekte des gleichen Typs generieren. Diese Befehlsobjekte stellen nur 3 Löschanforderungen dar. kein Löschalgorithmus. Da es sich bei Anfragen jetzt um eine Reihe von Objekten handelt, können wir sie problemlos verwalten. Plötzlich wird es trivial, Funktionen wie Rückgängig oder Wiederherstellen bereitzustellen.

Es spielt keine Rolle, wie der Befehl die angeforderte Logik implementiert. Beim Aufruf von execute () kann ein Algorithmus implementiert werden, um das Löschen auszulösen, oder er kann ihn sogar an andere Objekte delegieren oder sogar an eine Strategie delegieren. Es ist nur ein Implementierungsdetail des Befehlsmusters. Aus diesem Grund wird es als Befehl bezeichnet, obwohl es keine höfliche Art ist, Folgendes anzufordern : -)

Vergleichen Sie es mit der Strategie; Dieses Muster befasst sich nur mit der tatsächlichen Logik , die ausgeführt wird. Wenn wir das tun, hilft es, verschiedene Verhaltenskombinationen mit einer minimalen Anzahl von Klassen zu erzielen und so eine Klassenexplosion zu verhindern.

Ich denke, Command hilft uns, unser Verständnis von Kapselung zu erweitern, während Strategie die natürliche Verwendung von Kapselung und Polymorphismus ermöglicht.


15

Ich sehe es so, dass Sie mehrere Möglichkeiten haben, dasselbe zu tun. Jede davon ist eine Strategie, und zur Laufzeit bestimmt etwas, welche Strategie ausgeführt wird.

Versuchen Sie es zuerst mit StrategyOne. Wenn die Ergebnisse nicht gut genug sind, versuchen Sie es mit StrategyTwo ...

Befehle sind an bestimmte Dinge gebunden, die passieren müssen, wie z. B. TryToWalkAcrossTheRoomCommand. Dieser Befehl wird immer dann ausgelöst, wenn ein Objekt versuchen soll, durch den Raum zu gehen. In diesem Objekt werden jedoch möglicherweise StrategyOne und StrategyTwo ausgeführt, um zu versuchen, durch den Raum zu gehen.

Kennzeichen


2
RE: "Mehrere Möglichkeiten, dasselbe zu tun" - Dies scheint im Widerspruch zu einigen gängigen Beispielen für Strategie zu stehen. Insbesondere diejenigen, bei denen es Implementierungsklassen gibt, die Addition, Subtraktion, Multiplikation usw. ausführen. Vielleicht sind dies keine guten Beispiele?
Joshua Davis

1
@JoshuaDavis all diese "Substrate" sind Sonderfälle einer Strategie: arithmetische Operation . Sie haben gemeinsame Argumente (2 Operanden) und erzeugen als Ergebnis einen Wert. Je nach Implementierung machen sie das Gleiche (als Blackbox) auf ihre eigene Art und Weise. Ich sehe hier also keinen Konflikt, aber im Gegenteil: schönes Beispiel =)
jungle_mole

7

Ich könnte meiner Meinung nach falsch liegen, aber ich behandle den Befehl als Funktion zum Ausführen oder als Reaktion. Es sollten mindestens zwei Spieler vorhanden sein: derjenige, der die Aktion anfordert, und derjenige, der die Aktion ausführt. Die grafische Benutzeroberfläche ist ein typisches Beispiel für ein Befehlsmuster:

  • Alle Schaltflächen in der Anwendungssymbolleiste sind mit einer Aktion verknüpft.
  • Button ist in diesem Fall der Executor.
  • Aktion ist in diesem Fall der Befehl.

Der Befehl ist normalerweise an einen bestimmten Bereich oder Geschäftsbereich gebunden, jedoch nicht erforderlich: Möglicherweise verfügen Sie über Befehle, die eine Rechnung ausstellen, eine Rakete starten oder eine Datei entfernen, die dieselbe Schnittstelle (z. B. eine einzelne execute()Methode) in einer Anwendung implementiert . Oft sind Befehle in sich geschlossen, sodass sie vom Executor nichts benötigen, um die beabsichtigte Aufgabe zu verarbeiten (alle erforderlichen Informationen werden zum Zeitpunkt der Erstellung angegeben). Manchmal sind Befehle kontextsensitiv und sollten in der Lage sein, diesen Kontext zu erkennen (Der Backspace- Befehl sollte die Caret-Position im Text kennen, um das vorherige Zeichen korrekt zu entfernen. Der Rollback- Befehl sollte die aktuelle Transaktion für das Rollback ermitteln. ...).

Die Strategie ist etwas anders: Sie ist eher an einen Bereich gebunden. Die Strategie kann eine Regel zum Formatieren eines Datums (in UTC? Gebietsschemaspezifisch?) ("Datumsformatierer" -Strategie) oder zum Berechnen eines Quadrats für eine geometrische Figur ("Quadratrechner" -Strategie) definieren. Strategien sind in diesem Sinne Objekte im Fliegengewicht, die etwas als Eingabe ("Datum", "Zahl", ...) verwenden und auf ihrer Grundlage eine Entscheidung treffen. Vielleicht nicht die beste, aber gutes Beispiel für Strategie ist eine mit verbundenen javax.xml.transform.SourceSchnittstelle: je nachdem , ob das übergebene Objekt ist DOMSourceoder SAXSourceoder StreamSourcedie Strategie (= XSLT Transformator in diesem Fall) werden unterschiedliche Regeln gelten , sie zu verarbeiten. Die Implementierung kann einfach sein switchoder ein Muster der Verantwortungskette beinhalten .

In der Tat haben diese beiden Muster etwas gemeinsam: Befehle und Strategien kapseln Algorithmen innerhalb desselben semantischen Bereichs.


1
Ich behandle den Befehl als Rückruffunktion oder Reaktion. Es sollte mindestens zwei Spieler geben: einen, der die Aktion anfordert, und einen, der sie ausführt ... - Ich verstehe, was Sie sagen wollen, aber ich würde mich scheuen, das Wort "Rückruf" zu verwenden, weil es oft das Wort ist 'Rückruf' impliziert einen asynchronen Aufruf und Sie müssen keine asynchronen Aufrufe vornehmen, damit das Befehlsmuster nützlich ist. Ein typisches Beispiel: Microsoft Word. Das Klicken auf die Symbolleistenschaltfläche und das Drücken von Tastenkombinationen rufen keine asynchronen Befehle auf, aber wir können verstehen, wie das Befehlsmuster in diesem Fall nützlich wäre
Jim G.

Ich stimme zu, obwohl ich, wie Jim sagte, den Verweis auf Rückruf entfernen würde.
JARC

Danke, ich habe einige Erweiterungen vorgenommen. Lassen Sie mich wissen, wenn Sie zustimmen / nicht zustimmen.
dma_k

5

Befehl:

Grundlegende Bestandteile:

  1. Befehl deklariert eine Schnittstelle für abstrakte Befehle wieexecute()
  2. Der Empfänger weiß, wie ein bestimmter Befehl ausgeführt wird
  3. Invoker enthält ConcreteCommand , der ausgeführt werden muss
  4. Der Client erstellt den ConcreteCommand und weist den Empfänger zu
  5. ConcreteCommand definiert die Bindung zwischen Befehl und Empfänger

Arbeitsablauf:

Client ruft Invoker => Invoker ruft ConcreteCommand => ConcreteCommand- Aufrufe auf Receiver- Methode auf, die die abstrakte Command- Methode implementiert .

Vorteil : Änderungen an Befehl und Empfänger durch den Client sind nicht betroffen. Invoker bietet eine lose Kopplung zwischen Client und Empfänger. Sie können mehrere Befehle mit demselben Invoker ausführen.

Mit dem Befehlsmuster können Sie einen Befehl auf verschiedenen Empfängern ausführen,indem Sie denselben Invoker verwenden . Invoker kennt den Empfängertyp nicht

Schauen Sie sich zum besseren Verständnis der Konzepte diesen JournalDev- Artikel von Pankaj Kumar und den dzone- Artikel an von James Sugrue zusätzlich zum Wikipedia-Link.

Sie können das Befehlsmuster verwenden , um

  1. Entkoppeln Sie den Aufrufer und den Befehlsempfänger

  2. Rückrufmechanismus implementieren

  3. Implementieren Sie die Funktionen zum Rückgängigmachen und Wiederherstellen

  4. Pflegen Sie eine Befehlsgeschichte

java.lang.Threadist eine gute Umsetzung von Befehlsmustern. Sie können Thread als Aufrufer und Klasse behandeln, die Runnable als ConcreteCommonad / Receiver und die run()Methode als Befehl implementieren .

Die Version des Befehlsmusters zum Rückgängigmachen / Wiederherstellen kann im Artikel von Theodore Norvell gelesen werden

Strategie:

Das Strategiemuster ist sehr einfach zu verstehen. Verwenden Sie dieses Muster, wenn

Sie haben mehrere Implementierungen für einen Algorithmus, und die Implementierung des Algorithmus kann sich zur Laufzeit abhängig von bestimmten Bedingungen ändern .

Nehmen Sie ein Beispiel für die Tarifkomponente des Airline-Buchungssystems

Die Fluggesellschaften möchten in verschiedenen Zeiträumen unterschiedliche Tarife anbieten - Spitzen- und Nebenzeiten. An Tagen außerhalb der Hauptverkehrszeiten möchte es die Nachfrage durch attraktive Rabatte anregen.

Wichtige Erkenntnisse zum Strategiemuster :

  1. Es ist ein Verhaltensmuster
  2. Es basiert auf Delegation
  3. Es ändert die Eingeweide des Objekts, indem es das Methodenverhalten ändert
  4. Es wird verwendet, um zwischen einer Familie von Algorithmen zu wechseln
  5. Es ändert das Verhalten des Objekts zur Laufzeit

Verwandte Beiträge mit Codebeispielen:

Verwenden des Befehlsentwurfsmusters

Beispiel aus der Praxis für das Strategiemuster


0

Für mich liegt der Unterschied in der Absicht. Die Implementierungen beider Muster sind ziemlich ähnlich, haben aber unterschiedliche Zwecke:

  • Bei einer Strategie weiß die Komponente, die das Objekt verwendet, was das Objekt tut (und verwendet es, um einen Teil seiner eigenen Arbeit auszuführen), aber es ist ihm egal, wie es es tut.

  • Bei einem Befehl weiß die Komponente, die das Objekt verwendet, weder, was der Befehl tut, noch wie er es tut - sie weiß nur, wie sie ihn aufruft. Die Aufgabe des Anrufers besteht lediglich darin, den Befehl auszuführen. Die vom Befehl ausgeführte Verarbeitung ist nicht Teil der Kernarbeit des Anrufers.

Dies ist der Unterschied - weiß das Objekt, das die Komponente verwendet, tatsächlich oder kümmert es sich darum, was die Komponente tut? Meistens kann dies basierend darauf bestimmt werden, ob das Musterobjekt einen Wert an seinen Aufrufer zurückgibt. Wenn sich der Aufrufer darum kümmert, was das Musterobjekt tut, möchte er wahrscheinlich, dass es etwas zurückgibt, und es wird eine Strategie sein. Wenn es sich nicht um einen Rückgabewert handelt, handelt es sich wahrscheinlich um einen Befehl (Hinweis: So etwas wie ein Java Callable ist immer noch ein Befehl, da der Aufrufer den Wert nicht zurückgibt, obwohl er einen Wert zurückgibt - er gibt ihn einfach zurück zu dem, was ursprünglich den Befehl geliefert hat).

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.