In der objektorientierten Programmierung ist Kommando (auch Befehl; englischcommand) 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.
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
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.[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{// KonkreterBefehlpublic: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;// DreierregelconstEinfacherBefehl&operator=(constEinfacherBefehl&)=delete;// implementiert fuehreAus durch Aufrufen der entsprechenden Operation(en) beim Empfänger.virtualvoidfuehreAus(){(empfaenger->*operation)();}private:Empfaenger*empfaenger;Operationoperation;};classMeineKlasse{// Empfängerpublic:// 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
publicabstractclassBefehl{publicabstractvoidfuehreAus();}//KonkreterBefehlpublicclassLichtSchalterextendsBefehl{privateLichtlicht;privatebooleanlichtIstAn;publicLichtSchalter(Lichtlicht){this.licht=licht;}@OverridepublicvoidfuehreAus(){if(lichtIstAn){licht.lichtAus();lichtIstAn=false;}else{licht.lichtAn();lichtIstAn=true;}}}importjava.util.ArrayList;importjava.util.List;//AufruferpublicclassFernbedienung{privateList<Befehl>history;publicFernbedienung(){history=newArrayList<>();}publicvoidknopfDruecken(Befehlbefehl){history.add(befehl);befehl.fuehreAus();}}//EmpfängerpublicclassLicht{publicLicht(){}publicvoidlichtAn(){System.out.println("Licht ist an.");}publicvoidlichtAus(){System.out.println("Licht ist aus.");}}//KlientpublicclassBewohner{privatestaticLichtlicht=newLicht();privatestaticLichtSchalterlichtSchalter=newLichtSchalter(licht);publicstaticvoidmain(String[]args){Fernbedienungfernbedienung=newFernbedienung();fernbedienung.knopfDruecken(lichtSchalter);}}
Beispiel in PHP
abstractclassKommando{abstractfunctionausfuehren();}classAufrufer{private$history=array();publicfunctionspeichernUndAusfuehren(Kommando$cmd){$this->history[]=$cmd;// optional$cmd->ausfuehren();}}// EmpfängerclassLicht{publicfunctionlicht_an(){write_line('Licht ist an.');}publicfunctionlicht_aus(){write_line('Licht ist aus.');}}// konkretes Kommando #1: Licht anclassKommando_AnextendsKommando{private$dasLicht;publicfunction__construct(Licht$licht){$this->dasLicht=$licht;}publicfunctionausfuehren(){$this->dasLicht->licht_an();}}// konkretes Kommando #2: Licht ausclassKommando_AusextendsKommando{private$dasLicht;publicfunction__construct(Licht$licht){$this->dasLicht=$licht;}publicfunctionausfuehren(){$this->dasLicht->licht_aus();}}// Der KlientfunctionTest($kommando_string){$lamp=newLicht();$kmd_an=newKommando_An($lamp);$kmd_aus=newKommando_Aus($lamp);$aufrufer=newAufrufer();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.');}}functionwrite_line($text){print$text.'<br/>';}Test('ON');Test('OFF');
Beispiel in TypeScript
enumCOM{ON,OFF}abstractclassKommando{ausfuehren():void{};}classAufrufer{privatehistory=[];speichernUndAusfuehren(cmd:Kommando):void{this.history.push(cmd);// optionalcmd.ausfuehren();}}// EmpfängerclassLicht{constructor(){}licht_an():void{write_line('Licht ist an.');}licht_aus():void{write_line('Licht ist aus.');}}// konkretes Kommando #1: Licht anclassKommando_AnextendsKommando{privatedasLicht:Licht;constructor(licht:Licht){super();this.dasLicht=<Licht>licht;}ausfuehren():void{this.dasLicht.licht_an();}}// konkretes Kommando #2: Licht ausclassKommando_AusextendsKommando{privatedasLicht:Licht;constructor(licht:Licht){super();this.dasLicht=<Licht>licht;}ausfuehren():void{this.dasLicht.licht_aus();}}// Der KlientfunctionTest(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);