Kommando (Entwurfsmuster)
In der objektorientierten Programmierung ist Kommando (auch Befehl; englisch command) ein Entwurfsmuster, das zur Kategorie der Verhaltensmuster (englisch behavioral design patterns) 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.[1] Es ist eines der GoF-Muster.
Verwendung
[Bearbeiten | Quelltext bearbeiten ]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 (callback function) 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 (undo ): 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 (redo) zu implementieren, genügt ein zweiter Stapel für die rückgängig gemachten Befehle.
Akteure
[Bearbeiten | Quelltext bearbeiten ]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
[Bearbeiten | Quelltext bearbeiten ]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++
[Bearbeiten | Quelltext bearbeiten ]Diese C++14 Implementierung basiert auf der vor C++98 Implementierung im Buch Entwurfsmuster.[2]
#include<iostream> #include<memory> classBefehl{ public: // deklariert eine Schnittstelle zum Ausführen einer Operation. virtualvoidfuehreAus()=0; virtual~Befehl()=default; protected: Befehl()=default; }; template<typenameEmpfaenger> classEinfacherBefehl:publicBefehl{// KonkreterBefehl public: typedefvoid(Empfaenger::*Operation)(); // definiert die Anbindung eines Empfängers an eine Aktion. EinfacherBefehl(std::shared_ptr<Empfaenger>empfaenger_,Operationoperation_): empfaenger(empfaenger_.get()),operation(operation_){} EinfacherBefehl(constEinfacherBefehl&)=delete;// Dreierregel constEinfacherBefehl&operator=(constEinfacherBefehl&)=delete; // implementiert fuehreAus durch Aufrufen der entsprechenden Operation(en) beim Empfänger. virtualvoidfuehreAus(){ (empfaenger->*operation)(); } private: Empfaenger*empfaenger; Operationoperation; }; classMeineKlasse{// Empfänger public: // weiss, wie die an die Ausführung einer Anfrage gebundenen Operationen auszuführen sind. Jede Klasse kann ein Empfänger sein. voidaktion(){ std::cout<<"MeineKlasse::aktion\n"; } }; intmain(){ // 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(); }
Die Programmausgabe ist:
MeineKlasse::aktion
Beispiel in Java
[Bearbeiten | Quelltext bearbeiten ]publicabstractclass Befehl{ publicabstractvoidfuehreAus(); } //KonkreterBefehl publicclass LichtSchalterextendsBefehl{ privateLichtlicht; privatebooleanlichtIstAn; publicLichtSchalter(Lichtlicht){ this.licht=licht; } @Override publicvoidfuehreAus(){ if(lichtIstAn){ licht.lichtAus(); lichtIstAn=false; } else{ licht.lichtAn(); lichtIstAn=true; } } } importjava.util.ArrayList; importjava.util.List; //Aufrufer publicclass Fernbedienung{ privateList<Befehl>history; publicFernbedienung(){ history=newArrayList<>(); } publicvoidknopfDruecken(Befehlbefehl){ history.add(befehl); befehl.fuehreAus(); } } //Empfänger publicclass Licht{ publicLicht(){ } publicvoidlichtAn(){ System.out.println("Licht ist an."); } publicvoidlichtAus(){ System.out.println("Licht ist aus."); } } //Klient publicclass Bewohner{ privatestaticLichtlicht=newLicht(); privatestaticLichtSchalterlichtSchalter=newLichtSchalter(licht); publicstaticvoidmain(String[]args){ Fernbedienungfernbedienung=newFernbedienung(); fernbedienung.knopfDruecken(lichtSchalter); } }
Beispiel in PHP
[Bearbeiten | Quelltext bearbeiten ]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.'<br/>'; } Test('ON'); Test('OFF');
Beispiel in TypeScript
[Bearbeiten | Quelltext bearbeiten ]enumCOM{ ON, OFF } abstractclassKommando{ ausfuehren():void{ }; } classAufrufer{ privatehistory=[]; speichernUndAusfuehren(cmd:Kommando):void{ this.history.push(cmd);// optional cmd.ausfuehren(); } } // Empfänger classLicht{ constructor(){} licht_an():void{ write_line('Licht ist an.'); } licht_aus():void{ write_line('Licht ist aus.'); } } // konkretes Kommando #1: Licht an classKommando_AnextendsKommando{ privatedasLicht:Licht; constructor(licht:Licht){ super(); this.dasLicht=<Licht>licht; } ausfuehren():void{ this.dasLicht.licht_an(); } } // konkretes Kommando #2: Licht aus classKommando_AusextendsKommando{ privatedasLicht:Licht; constructor(licht:Licht){ super(); this.dasLicht=<Licht>licht; } ausfuehren():void{ this.dasLicht.licht_aus(); } } // Der Klient functionTest(kommando_string:string|number):void{ constlamp:Licht=newLicht(); constkmd_an:Kommando=newKommando_An(lamp); constkmd_aus:Kommando=newKommando_Aus(lamp); constaufrufer:Aufrufer=newAufrufer(); switch(kommando_string){ case1: case'ON': aufrufer.speichernUndAusfuehren(kmd_an); break; case0: case'OFF': aufrufer.speichernUndAusfuehren(kmd_aus); break; default: write_line('Nur die Argumente "ON" oder "OFF" sind erlaubt.'); } } functionwrite_line(text:string){ console.log(text); } Test('ON'); Test('OFF'); Test(COM.ON); Test(COM.OFF);
Ausgabe:
Licht ist an. Licht ist aus.
Weblinks
[Bearbeiten | Quelltext bearbeiten ]Einzelnachweise
[Bearbeiten | Quelltext bearbeiten ]- ↑ Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides: Entwurfsmuster. 5. Auflage. Addison-Wesley, 1996, ISBN 3-8273-1862-9, S. 273 ff.
- ↑ Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides: Entwurfsmuster. 5. Auflage. Addison-Wesley, Bonn 1996, ISBN 3-8273-1862-9, S. 278 ff.
Abstrakte Fabrik | Erbauer | Fabrikmethode | Prototyp | Singleton | Multiton | Objektpool
Adapter | Brücke | Decorator | Fassade | Fliegengewicht | Kompositum | Stellvertreter
Beobachter | Besucher | Interpreter | Iterator | Kommando | Memento | Schablonenmethode | Strategie | Vermittler | Zustand | Zuständigkeitskette | Interceptor | Nullobjekt | Protokollstapel
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
übermittlungsmuster
File Transfer | Shared Database | Remote Procedure Invocation | Messaging
Message | Command Message | Document Message | Event Message | Request-Reply | Return Address | Correlation Identifier | Message Sequence | Message Expiration | Format Indicator
Message Endpoint | Messaging Gateway | Messaging Mapper | Transactional Client | Polling Consumer | Event-driven Consumer | Competing Consumers | Message Dispatcher | Selective Consumer | Durable Subscriber | Idempotent Receiver | Service Activator
Message Channel | Point-to-Point Channel | Publisher-Subscriber Channel | Datatype Channel | Invalid Message Channel | Dead Letter Channel | Guaranteed Delivery | Channel Adapter | Messaging Bridge | Message Bus
Pipes-and-Filters | Message Router | Content-based Router | Message Filter | Dynamic Router | Recipient List | Splitter | Aggregator | Resequencer | Composed Message Processor | Scatter-Gather | Routing Slip | Process Manager | Message Broker
Message Translator | Envelope Wrapper | Content Enricher | Content Filter | Claim Check | Normalizer | Canonical Data Model
Control Bus | Detour | Wire Tap | Message History | Message Store | Smart Proxy | Test Message | Channel Purger
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