Command (Design Pattern): Unterschied zwischen den Versionen

Aus Byte-Welt Wiki
Zur Navigation springenZur Suche springen
K
 
(2 dazwischenliegende Versionen von 2 Benutzern werden nicht angezeigt)
Zeile 1: Zeile 1:
Das Command Design Pattern kapselt eine Anfrage als ein Objekt, so dass wir unsere Kunden mit unterschiedlichen Anfragen parametrieren können, Anfragen in einer Warteschlange oder einem Journal ablegen können, und aufhebbare Operationen unterstützen können.
+
Das Command Design Pattern kapselt eine Anfrage als ein [[Objekt]], so dass wir unsere Kunden mit unterschiedlichen Anfragen parametrieren können, Anfragen in einer Warteschlange oder einem Journal ablegen können, und aufhebbare Operationen unterstützen können.
  
Das Command Design Pattern entkoppelt ein Objekt das eine Anfrage macht, von demjenigen der weiß, wie diese Anfrage auszuführen ist. Im Mittelpunkt dieser Entkopplung steht ein Kommando-Objekt, das einen Empfänger und eine Aktion (oder eine Reihe von Aktionen) kapselt. Ein Aufrufer (invoker) richtet eine Anfrage an ein Kommando-Objekt durch den Aufruf seiner execute()-Methode, die wiederum die Aktionen des Empfängers (receiver) aufruft. Aufrufer können mit unterschiedlichen Befehlen konfiguriert werden, auch dynamisch zur Laufzeit.
+
Das Command Design Pattern entkoppelt ein Objekt das eine Anfrage macht, von demjenigen der weiß, wie diese Anfrage auszuführen ist. Im Mittelpunkt dieser Entkopplung steht ein Kommando-Objekt, das einen Empfänger und eine Aktion (oder eine Reihe von Aktionen) kapselt. Ein Aufrufer (invoker) richtet eine Anfrage an ein Kommando-Objekt durch den Aufruf seiner execute()-[[Methode]], die wiederum die Aktionen des Empfängers (receiver) aufruft. Aufrufer können mit unterschiedlichen Befehlen konfiguriert werden, auch dynamisch zur Laufzeit.
  
Die Befehle können auch eine "undo"-Funktion unterstützen (rückgängig machen), indem sie eine undo()-Methode umsetzen, die das Empfänger-Objekt wieder in seinen vorherigen Zustand bringt, d.h. in den Zustand vor dem letzten Aufruf der Methode execute().
+
Die Befehle können auch eine "undo"-Funktion unterstützen (rückgängig machen), indem sie eine undo()-Methode umsetzen, die das Empfänger-Objekt wieder in seinen vorherigen Zustand bringt, d.h. in den Zustand vor dem letzten Aufruf der [[Methode]] execute().
  
 
Ein Makro-Kommando ist eine einfache Erweiterung eines Kommandos, so dass er mehrere Befehle aufrufen kann. Ebenso kann ein Makro-Kommando die undo-Funktion leicht unterstützen.
 
Ein Makro-Kommando ist eine einfache Erweiterung eines Kommandos, so dass er mehrere Befehle aufrufen kann. Ebenso kann ein Makro-Kommando die undo-Funktion leicht unterstützen.
Zeile 11: Zeile 11:
 
Stellen wir uns zum Beispiel einen Empfänger "Prozessor" vor, der arithmetische Operationen durchführen kann:
 
Stellen wir uns zum Beispiel einen Empfänger "Prozessor" vor, der arithmetische Operationen durchführen kann:
  
<code=java>
+
<syntaxhighlight lang="java">
 
package command;
 
package command;
  
Zeile 29: Zeile 29:
 
   /**
 
   /**
 
     * Berechnung von arithmetischen Operationen, wobei das Resultat kumuliert wird.
 
     * Berechnung von arithmetischen Operationen, wobei das Resultat kumuliert wird.
     * Das aktuelle Resultat wir auf dem Bildschirm angezeigt  
+
     * Das aktuelle Resultat wird auf dem Bildschirm angezeigt  
     * und kann mit der Methode getResultat() abgefragt werden.
+
     * und kann mit Hilfe der Methode getResultat() abgefragt werden.
 
     * @param operator Addition=+, Substraktion=-, Multiplikation=* Division=/
 
     * @param operator Addition=+, Substraktion=-, Multiplikation=* Division=/
 
     * @param operand eine Zahl vom Typ "double"
 
     * @param operand eine Zahl vom Typ "double"
Zeile 67: Zeile 67:
 
   }
 
   }
 
}
 
}
</code=java>
+
</syntaxhighlight>
  
Die Schnittstelle "Command" ermöglicht es eine Aktion auf dem Prozessor auszuführen und rückgängig zu machen. Sie ist sehr einfach:
+
Die [[Schnittstelle]] "Command" ermöglicht es, eine Aktion auf dem Prozessor auszuführen und rückgängig zu machen. Sie ist sehr einfach:
 
+
<syntaxhighlight lang="java">
<code=java>
 
 
package command;
 
package command;
  
//Die Schnittstelle Command wird von den Klassen MacroCommand, Berechnung und Reset umgesetzt.
+
//Die Schnittstelle Command wird von den Klassen Berechnung, Reset und MacroCommand umgesetzt.
 
public interface Command
 
public interface Command
 
{
 
{
Zeile 82: Zeile 81:
 
   public void undo();
 
   public void undo();
 
}
 
}
</code=java>
+
</syntaxhighlight>
 
 
Klasse "Berechnung" ist ein konkreter Befehl, um eine arithmetische Operation zu berechnen:
 
  
<code=java>
+
Die [[Klasse]] "Berechnung" ist ein konkreter Befehl, der eine arithmetische Operation berechnet:
 +
<syntaxhighlight lang="java">
 
package command;
 
package command;
  
//eine konkreter Befehl, um eine arithmetische Operation zu berechnen
+
//eine konkreter Befehl, der eine arithmetische Operation berechnet
 
public class Berechnung implements Command
 
public class Berechnung implements Command
 
{
 
{
Zeile 136: Zeile 134:
 
   }
 
   }
 
}
 
}
</code=java>
+
</syntaxhighlight>
  
Die Klasse "Reset" ist auch ein konkreter Befehl, der den Prozessor zurücksetzt:
+
Die [[Klasse]] "Reset" ist auch ein konkreter Befehl, der den Prozessor zurücksetzt:
  
<code=java>
+
<syntaxhighlight lang="java">
 
package command;
 
package command;
  
Zeile 174: Zeile 172:
 
   }
 
   }
 
}
 
}
</code=java>
+
</syntaxhighlight>
  
Die Klasse "MacroCommand" kann eine Reihe von Befehlen ausführen, die als Parameter übergeben werden:
+
Die [[Klasse]] "MacroCommand" kann eine Reihe von Befehlen ausführen, die als Parameter übergeben werden:
  
<code=java>
+
<syntaxhighlight lang="java">
 
package command;
 
package command;
  
Zeile 222: Zeile 220:
 
   }
 
   }
 
}
 
}
</code=java>
+
</syntaxhighlight>
  
Die Klasse "Taschenrechner" ist ein Aufrufer von Befehlen; sie weiß, wie man Befehle ausführen und rückgängig machen kann:
+
Die [[Klasse]] "Taschenrechner" ist ein Aufrufer von Befehlen; sie weiß, wie man Befehle ausführen und rückgängig machen kann:
  
<code=java>
+
<syntaxhighlight lang="java">
 
package command;
 
package command;
  
Zeile 248: Zeile 246:
  
 
   /**
 
   /**
     * Den Befehl ausführen, der als Parameter übergebn wird.
+
     * Den Befehl ausführen, der als Parameter übergeben wird.
 
     *
 
     *
 
     * @param command
 
     * @param command
Zeile 309: Zeile 307:
 
   }
 
   }
 
}
 
}
</code=java>
+
</syntaxhighlight>
  
Und schließlich ist hier die Starter-Klasse, zum Testen unseres kleinen Taschenrechners, der dynamisch mit verschiedenen Befehlen konfigurierbar ist:
+
Und schließlich ist hier die Starter-[[Klasse]], zum Testen unseres kleinen Taschenrechners, der dynamisch mit verschiedenen Befehlen konfigurierbar ist:
  
<code=java>
+
<syntaxhighlight lang="java">
 
package command;
 
package command;
  
Zeile 345: Zeile 343:
 
   }
 
   }
 
}
 
}
</code=java>
+
</syntaxhighlight>
  
 
Der Taschenrechner gibt dies auf der Konsole aus:
 
Der Taschenrechner gibt dies auf der Konsole aus:
Zeile 351: Zeile 349:
 
[[Datei:Taschenrechner.jpg]]
 
[[Datei:Taschenrechner.jpg]]
  
[[Kategorie:Java]]
 
 
[[Kategorie:Design Pattern]]
 
[[Kategorie:Design Pattern]]
 
[[Kategorie:Design Patterns]]
 
[[Kategorie:Design Patterns]]
 
[[Kategorie:Gang of Four Pattern]]
 
[[Kategorie:Gang of Four Pattern]]

Aktuelle Version vom 8. April 2018, 11:55 Uhr

Das Command Design Pattern kapselt eine Anfrage als ein Objekt, so dass wir unsere Kunden mit unterschiedlichen Anfragen parametrieren können, Anfragen in einer Warteschlange oder einem Journal ablegen können, und aufhebbare Operationen unterstützen können.

Das Command Design Pattern entkoppelt ein Objekt das eine Anfrage macht, von demjenigen der weiß, wie diese Anfrage auszuführen ist. Im Mittelpunkt dieser Entkopplung steht ein Kommando-Objekt, das einen Empfänger und eine Aktion (oder eine Reihe von Aktionen) kapselt. Ein Aufrufer (invoker) richtet eine Anfrage an ein Kommando-Objekt durch den Aufruf seiner execute()-Methode, die wiederum die Aktionen des Empfängers (receiver) aufruft. Aufrufer können mit unterschiedlichen Befehlen konfiguriert werden, auch dynamisch zur Laufzeit.

Die Befehle können auch eine "undo"-Funktion unterstützen (rückgängig machen), indem sie eine undo()-Methode umsetzen, die das Empfänger-Objekt wieder in seinen vorherigen Zustand bringt, d.h. in den Zustand vor dem letzten Aufruf der Methode execute().

Ein Makro-Kommando ist eine einfache Erweiterung eines Kommandos, so dass er mehrere Befehle aufrufen kann. Ebenso kann ein Makro-Kommando die undo-Funktion leicht unterstützen.

Kommandos können auch für die Umsetzung von Transaktions- und Logging-Systemen verwendet werden.

Stellen wir uns zum Beispiel einen Empfänger "Prozessor" vor, der arithmetische Operationen durchführen kann:

package command;

public class Prozessor
{

   private double resultat;
   private String operator;
   private double operand;
   
   /**
    * Ein Prozessor für arithmetische Operationen
    */
   public Prozessor(){
   }

   /**
    * Berechnung von arithmetischen Operationen, wobei das Resultat kumuliert wird.
    * Das aktuelle Resultat wird auf dem Bildschirm angezeigt 
    * und kann mit Hilfe der Methode getResultat() abgefragt werden.
    * @param operator Addition=+, Substraktion=-, Multiplikation=* Division=/
    * @param operand eine Zahl vom Typ "double"
    */
   public void berechnung(final String operator, final double operand)
   {
      this.operator = operator;
      this.operand = operand;
      switch (operator)
      {
         case "+":
            resultat += operand;
            break;
         case "-":
            resultat -= operand;
            break;
         case "*":
            resultat *= operand;
            break;
         case "/":
            resultat /= operand;
            break;
      }
      System.out.println(this);
   }

   public double getResultat()
   {
      return resultat;
   }

   @Override
   public String toString()
   {
      return operator + " " + operand + " = " + resultat;
   }
}

Die Schnittstelle "Command" ermöglicht es, eine Aktion auf dem Prozessor auszuführen und rückgängig zu machen. Sie ist sehr einfach:

package command;

//Die Schnittstelle Command wird von den Klassen Berechnung, Reset und MacroCommand umgesetzt.
public interface Command
{

   public void execute();

   public void undo();
}

Die Klasse "Berechnung" ist ein konkreter Befehl, der eine arithmetische Operation berechnet:

package command;

//eine konkreter Befehl, der eine arithmetische Operation berechnet
public class Berechnung implements Command
{

   private Prozessor prozessor;
   private String operator;
   private double operand;

   public Berechnung(final Prozessor prozessor, final String operator, final double operand)
   {
      this.prozessor = prozessor;
      this.operator = operator;
      this.operand = operand;
   }

   @Override
   public void execute()
   {
      prozessor.berechnung(operator, operand);
   }

   @Override
   public void undo()
   {
      switch (operator)
      {
         case "+":
            prozessor.berechnung("-", operand);
            break;
         case "-":
            prozessor.berechnung("+", operand);
            break;
         case "*":
            prozessor.berechnung("/", operand);
            break;
         case "/":
            prozessor.berechnung("*", operand);
            break;
      }
   }

   @Override
   public String toString()
   {
      return operator + "" + operand;
   }
}

Die Klasse "Reset" ist auch ein konkreter Befehl, der den Prozessor zurücksetzt:

package command;

//ein konkreter Befehl, der den Prozessor zurücksetzt:
public class Reset implements Command
{

   private Prozessor prozessor;
   private double resultat;

   public Reset(final Prozessor prozessor)
   {
      this.prozessor = prozessor;
   }

   @Override
   public void execute()
   {
      resultat = prozessor.getResultat();
      prozessor.berechnung("-", resultat);
   }

   @Override
   public void undo()
   {
      prozessor.berechnung("+", resultat);
   }

   @Override
   public String toString()
   {
      return "Reset{" + resultat + '}';
   }
}

Die Klasse "MacroCommand" kann eine Reihe von Befehlen ausführen, die als Parameter übergeben werden:

package command;

import java.util.Arrays;

public class MacroCommand implements Command
{

   private final Command[] commands;

   /**
    * Ein MacroCommand kann eine Reihe von Befehlen ausführen, die als Parameter übergeben werden.
    *
    * @param commands eine Reihe von Befehlen
    */
   public MacroCommand(final Command[] commands)
   {
      this.commands = commands;
   }

   @Override
   public void execute()
   {
      for (int i = 0; i < commands.length; i++)
      {
         commands[i].execute();
      }
   }

   @Override
   public void undo()
   {
      for (int i = commands.length - 1; i >= 0; i--)
      {
         commands[i].undo();
      }
   }

   @Override
   public String toString()
   {
      return Arrays.toString(commands);
   }
}

Die Klasse "Taschenrechner" ist ein Aufrufer von Befehlen; sie weiß, wie man Befehle ausführen und rückgängig machen kann:

package command;

import java.util.ArrayList;
import java.util.List;

public class Taschenrechner
{

   private List<Command> undoable;
   private int undoLevel;

   /**
    * Ein Taschenrechner der Befehle ausführen und rückgängig machen kann.
    */
   public Taschenrechner()
   {
      undoable = new ArrayList<>(10);
      undoLevel = 0;
   }

   /**
    * Den Befehl ausführen, der als Parameter übergeben wird.
    *
    * @param command
    */
   public void execute(final Command command)
   {
      System.out.print("Berechnung: ");
      command.execute();
      addUndoable(command);
   }

   /**
    * Die als Parameter übergebene Anzahl von Befehlen rückgängig machen.
    *
    * @param number
    */
   public void undo(final int number)
   {
      System.out.println("undo(" + number + "):");
      for (int i = 0; i < number; i++)
      {
         if (undoLevel > 0)
         {
            Command commande = undoable.get(--undoLevel);
            commande.undo();
         }
      }
   }

   /**
    * Die als Parameter übergebene Anzahl von Befehlen wieder ausführen.
    *
    * @param number
    */
   public void redo(final int number)
   {
      System.out.println("redo(" + number + "):");
      for (int i = 0; i < number; i++)
      {
         if (undoLevel < undoable.size())
         {
            Command commande = undoable.get(undoLevel++);
            commande.execute();
         }
      }
   }

   //private Servicemethode:
   private void addUndoable(final Command command)
   {
      //die "undoable"-Liste dem aktuellen undoLevel anpassen:
      int count = undoable.size() - undoLevel;
      for (int i = 0; i < count; i++)
      {
         undoable.remove(undoable.size() - 1);
      }
      //den neuen Befehl hinzufügen:
      undoable.add(command);
      undoLevel = undoable.size();
   }
}

Und schließlich ist hier die Starter-Klasse, zum Testen unseres kleinen Taschenrechners, der dynamisch mit verschiedenen Befehlen konfigurierbar ist:

package command;

public class Demo
{

   public static void main(final String[] args)
   {
      Prozessor prozessor = new Prozessor();
      Taschenrechner taschenrechner = new Taschenrechner();
      
      taschenrechner.execute(new Berechnung(prozessor, "-", 4));
      taschenrechner.execute(new Berechnung(prozessor, "*", 3));
      taschenrechner.execute(new Berechnung(prozessor, "+", 14));
      double resultat = prozessor.getResultat();
      
      Command[] pi = new Command[]
      {
         new Reset(prozessor),
         new Berechnung(prozessor, "+", 103993),
         new Berechnung(prozessor, "/", 33102),
      };
      
      taschenrechner.execute(new MacroCommand(pi));
      taschenrechner.execute(new Berechnung(prozessor, "+", resultat));

      taschenrechner.undo(2);
      taschenrechner.redo(1);
      
      taschenrechner.execute(new Reset(prozessor));
   }
}

Der Taschenrechner gibt dies auf der Konsole aus:

Taschenrechner.jpg