Zum Inhalt springen

Kommando (Entwurfsmuster)

aus Wikipedia, der freien Enzyklopädie
Dies ist die aktuelle Version dieser Seite, zuletzt bearbeitet am 20. April 2023 um 10:54 Uhr durch imported>AndreAdrian (Absatz Beispiel in C++ dazu).
(Unterschied) ← Nächstältere Version | Aktuelle Version (Unterschied) | Nächstjüngere Version → (Unterschied)

In der objektorientierten Programmierung ist Kommando (auch Befehl; {{Modul:Vorlage:lang}} Modul:Vorlage:lang:103: attempt to index field 'wikibase' (a nil value)) ein Entwurfsmuster, das zur Kategorie der Verhaltensmuster (englisch {{Modul:Vorlage:lang}} Modul:Multilingual:153: attempt to index field 'data' (a nil value)) gehört. In diesem Entwurfsmuster kapselt das Kommando-Objekt einen Befehl, um es so zu ermöglichen, Operationen in eine Warteschlange zu stellen, Logbucheinträge zu führen und Operationen rückgängig zu machen.<ref>Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides: Entwurfsmuster. 5. Auflage. Addison-Wesley, 1996, ISBN 3-8273-1862-9, S. 273 ff.</ref> Es ist eines der GoF-Muster.

Verwendung

Wenn z. B. eine Schaltfläche in einer grafischen Benutzeroberfläche mit einer Aktion verknüpft werden soll, dient das Kommando dazu, die auszuführende Aktion zu parametrisieren. Es stellt somit die objektorientierte Entsprechung zu den Rückruffunktionen ({{Modul:Vorlage:lang}} Modul:Multilingual:153: attempt to index field 'data' (a nil value)) dar. Dabei können das Erstellen des Kommandos und die tatsächliche Ausführung zu verschiedenen Zeiten oder in einem anderen Kontext (Thread, Prozess, Rechner) stattfinden.

Implementierung eines Rückgängig-Mechanismus ({{Modul:Vorlage:lang}} Modul:Multilingual:153: attempt to index field 'data' (a nil value)): Bei jeder Ausführung werden die zur Umkehrung nötigen Daten im Objekt gespeichert und das Objekt selber auf einem Stapel gesichert. Um das Gegenteil Wiederherstellen ({{Modul:Vorlage:lang}} Modul:Multilingual:153: attempt to index field 'data' (a nil value)) zu implementieren, genügt ein zweiter Stapel für die rückgängig gemachten Befehle.

Akteure

Datei:Command pattern de.svg
UML-Klassendiagramm des Kommandos

Der Befehl ist die Basisklasse aller Kommandos. Ein konkreter Befehl speichert den zum Ausführen nötigen Zustand, darunter typischerweise auch einen Verweis auf den Empfänger und implementiert die Befehlsschnittstelle.

Der Klient erzeugt einen konkreten Befehl und versieht ihn mit einem Verweis auf den Empfänger und allen anderen nötigen Informationen. Er gibt dem Aufrufer eine Referenz auf den konkreten Befehl.

Der Aufrufer besitzt einen oder mehrere Verweise auf Befehle und fordert diese bei Bedarf auf, ihre Aktion auszuführen. An den Empfänger werden keine besonderen Anforderungen gestellt. Er muss nichts über die anderen Akteure wissen. Somit kann jede Klasse als Empfänger dienen. Der konkrete Befehl ruft Methoden des Empfängerobjektes auf, um seine Aktion auszuführen.

Vor- & Nachteile

Auslösender und Ausführender sind entkoppelt. Befehlsobjekte können wie andere Objekte auch manipuliert werden (Verändern, Filtern, Zwischenspeichern, …). Befehlsobjekte können zu komplexen Befehlen kombiniert werden (Makros, realisiert als Kompositum).

Da für jedes Kommando eine neue Klasse benötigt wird, kann deren Anzahl schnell groß und die Implementierung damit unübersichtlich werden.

Beispiel in C++

Diese C++14 Implementierung basiert auf der vor C++98 Implementierung im Buch Entwurfsmuster.<ref>Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides: Entwurfsmuster. 5. Auflage. Addison-Wesley, Bonn 1996, ISBN 3-8273-1862-9, S. 278 ff.</ref>

<syntaxhighlight lang="cpp">

  1. include <iostream>
  2. include <memory>

class Befehl { public:

 // deklariert eine Schnittstelle zum Ausführen einer Operation.
 virtual void fuehreAus() = 0;
 virtual ~Befehl() = default;

protected:

 Befehl() = default;

};

template <typename Empfaenger> class EinfacherBefehl : public Befehl { // KonkreterBefehl public:

 typedef void (Empfaenger::* Operation)();
 // definiert die Anbindung eines Empfängers an eine Aktion.
 EinfacherBefehl(std::shared_ptr<Empfaenger> empfaenger_, Operation operation_) :
   empfaenger(empfaenger_.get()), operation(operation_) { }
 EinfacherBefehl(const EinfacherBefehl&) = delete; // Dreierregel
 const EinfacherBefehl& operator=(const EinfacherBefehl&) = delete;
 // implementiert fuehreAus durch Aufrufen der entsprechenden Operation(en) beim Empfänger.
 virtual void fuehreAus() {
   (empfaenger->*operation)();
 }

private:

 Empfaenger* empfaenger;
 Operation operation;

};

class MeineKlasse { // Empfänger public:

 // weiss, wie die an die Ausführung einer Anfrage gebundenen Operationen auszuführen sind. Jede Klasse kann ein Empfänger sein.
 void aktion() {
   std::cout << "MeineKlasse::aktion\n";
 }

};

int main() {

 // Die Smart pointers verhindern Memory Leaks.
 std::shared_ptr<MeineKlasse> empfaenger = std::make_shared<MeineKlasse>();
 // ...
 std::unique_ptr<Befehl> einBefehl = std::make_unique<EinfacherBefehl<MeineKlasse> >(empfaenger, &MeineKlasse::aktion);
 // ...
 einBefehl->fuehreAus();

} </syntaxhighlight>

Die Programmausgabe ist:

<syntaxhighlight lang="cpp"> MeineKlasse::aktion </syntaxhighlight>

Beispiel in Java

<syntaxhighlight lang="java"> public abstract class Befehl {

   public abstract void fuehreAus();

}


//KonkreterBefehl public class LichtSchalter extends Befehl {

   private Licht licht;
   private boolean lichtIstAn;
   public LichtSchalter(Licht licht) {
       this.licht = licht;
   }
   @Override
   public void fuehreAus() {
       if (lichtIstAn) {
           licht.lichtAus();
           lichtIstAn = false;
       }
       else {
           licht.lichtAn();
           lichtIstAn = true;
       }
   }

}


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

//Aufrufer public class Fernbedienung {

   private List<Befehl> history;
   public Fernbedienung() {
       history = new ArrayList<>();
   }
   public void knopfDruecken(Befehl befehl) {
       history.add(befehl);
       befehl.fuehreAus();
   }

}


//Empfänger public class Licht {

   public Licht() {
   }
   public void lichtAn() {
       System.out.println("Licht ist an.");
   }
   public void lichtAus() {
       System.out.println("Licht ist aus.");
   }

}


//Klient public class Bewohner {

   private static Licht licht = new Licht();
   private static LichtSchalter lichtSchalter = new LichtSchalter(licht);
   public static void main(String[] args) {
       Fernbedienung fernbedienung = new Fernbedienung();
       fernbedienung.knopfDruecken(lichtSchalter);
   }

} </syntaxhighlight>

Beispiel in PHP

<syntaxhighlight lang="PHP"> abstract class Kommando {

   abstract function ausfuehren();

}

class Aufrufer {

   private $history = array();
   public function speichernUndAusfuehren(Kommando $cmd) {
       $this->history[] = $cmd; // optional
       $cmd->ausfuehren();
   }

}

// Empfänger class Licht {

   public function licht_an() {
       write_line('Licht ist an.');
   }
   public function licht_aus() {
       write_line('Licht ist aus.');
   }

}

// konkretes Kommando #1: Licht an class Kommando_An extends Kommando {

   private $dasLicht;
   public function __construct(Licht $licht) {
       $this->dasLicht = $licht;
   }
   public function ausfuehren() {
       $this->dasLicht->licht_an();
   }

}

// konkretes Kommando #2: Licht aus class Kommando_Aus extends Kommando {

   private $dasLicht;
   public function __construct(Licht $licht) {
       $this->dasLicht = $licht;
   }
   public function ausfuehren() {
       $this->dasLicht->licht_aus();
   }

}

// Der Klient function Test($kommando_string) {

   $lamp     = new Licht();
   $kmd_an   = new Kommando_An ($lamp);
   $kmd_aus  = new Kommando_Aus($lamp);
   $aufrufer = new Aufrufer();
   switch ($kommando_string) {
       case 'ON':
           $aufrufer->speichernUndAusfuehren($kmd_an);
       break;
       case 'OFF':
           $aufrufer->speichernUndAusfuehren($kmd_aus);
       break;
       default:
           write_line('Nur die Argumente "ON" oder "OFF" sind erlaubt.');
   }

}

function write_line($text) {

   print $text.'
';

}

Test('ON'); Test('OFF'); </syntaxhighlight>

Beispiel in TypeScript

<syntaxhighlight lang="typescript" line="1"> enum COM {

   ON,
   OFF

}

abstract class Kommando {

   ausfuehren() : void {
   };

}

class Aufrufer {

   private history = [];
   speichernUndAusfuehren(cmd: Kommando) : void {
       this.history.push(cmd); // optional
       cmd.ausfuehren();
   }

}

// Empfänger class Licht {

   constructor(){}
   licht_an() : void {
       write_line('Licht ist an.');
   }
   licht_aus() : void {
       write_line('Licht ist aus.');
   }

}

// konkretes Kommando #1: Licht an class Kommando_An extends Kommando {

   private dasLicht : Licht;
   constructor(licht: Licht) {
       super();
       this.dasLicht = <Licht> licht;
   }
   ausfuehren() : void {
       this.dasLicht.licht_an();
   }

}

// konkretes Kommando #2: Licht aus class Kommando_Aus extends Kommando {

   private dasLicht: Licht;
   constructor(licht: Licht) {
       super();
       this.dasLicht = <Licht> licht;
   }
   ausfuehren() : void {
       this.dasLicht.licht_aus();
   }

}

// Der Klient function Test(kommando_string : string|number) : void {

   const lamp : Licht     = new Licht();
   const kmd_an : Kommando  = new Kommando_An (lamp);
   const kmd_aus: Kommando  = new Kommando_Aus(lamp);
   const aufrufer: Aufrufer = new Aufrufer();
   switch (kommando_string) {
       case 1:
       case 'ON':
           aufrufer.speichernUndAusfuehren(kmd_an);
           break;
       case 0:
       case 'OFF':
           aufrufer.speichernUndAusfuehren(kmd_aus);
           break;
       default:
           write_line('Nur die Argumente "ON" oder "OFF" sind erlaubt.');
   }

}

function write_line(text: string) {

   console.log(text);

}

Test('ON'); Test('OFF'); Test(COM.ON); Test(COM.OFF); </syntaxhighlight>

Ausgabe:

Licht ist an.
Licht ist aus.

Weblinks

Commons: Kommando (Entwurfsmuster) – Sammlung von Bildern, Videos und Audiodateien

Einzelnachweise

<references />

<templatestyles src="Erweiterte Navigationsleiste/styles legacy.css" />Vorlage:Klappleiste/Anfang

Erzeugungsmuster

Abstrakte Fabrik | Erbauer | Fabrikmethode | Prototyp | Singleton | Multiton | Objektpool

Strukturmuster

Adapter | Brücke | Decorator | Fassade | Fliegengewicht | Kompositum | Stellvertreter

Verhaltensmuster

Beobachter | Besucher | Interpreter | Iterator | Kommando | Memento | Schablonenmethode | Strategie | Vermittler | Zustand | Zuständigkeitskette | Interceptor | Nullobjekt | Protokollstapel

Muster für objekt-
relationale Abbildung

Datentransferobjekt | Table Data Gateway | Row Data Gateway | Active Record | Unit of Work | Identity Map | Lazy Loading | Identity Field | Dependent Mapping | Embedded Value | Serialized LOB | Inheritance Mapper | Metadata Mapping | Query Object | Command-Query-Responsibility-Segregation

Nachrichten-
übermittlungsmuster
andere

Application Controller | Business Delegate | Data Access Object | Dependency Injection | Extension Interface | Fluent Interface | Inversion of Control (IoC) | Lock | Model View Controller (MVC) | Model View Presenter (MVP) | Model View Update (MVU) | Model View ViewModel (MVVM) | Page Controller | Registry | Remote Facade | Repository | Service Locator | Session State | Table Module | Template View | Threadpool | Transaction Script | Transform View | Two-Step View | Value Object

Vorlage:Klappleiste/Ende