Command-Muster
Teil des Abschnitts Objektorientierte Programmierung der Java-Journey von Coddy — Lektion 70 von 87.
Das Command Pattern ist ein Verhaltensmuster, das eine Anfrage in ein eigenständiges Objekt umwandelt, das alle Informationen über die Anfrage enthält. Diese Transformation ermöglicht es Ihnen, Anfragen als Methodenargumente zu übergeben, die Ausführung einer Anfrage zu verzögern oder in eine Warteschlange zu stellen und rückgängig machbare Operationen zu unterstützen.
Stellen Sie es sich wie ein Restaurant vor: Anstatt dem Koch direkt zu sagen, was er kochen soll, schreiben Sie Ihre Bestellung auf einen Zettel. Der Kellner (Invoker) bringt den Zettel (Command) in die Küche, wo er in eine Warteschlange eingereiht, priorisiert oder sogar storniert werden kann. Das Muster entkoppelt das Objekt, das die Operation aufruft, von demjenigen, das sie ausführt.
Das Muster besteht aus vier Schlüsselkomponenten: einem Command-Interface, das die execute-Methode deklariert, ConcreteCommand-Klassen, die spezifische Aktionen implementieren, einem Receiver, der die eigentliche Arbeit verrichtet, und einem Invoker, der Befehle auslöst:
interface Command {
void execute();
}class Light { // Empfänger
public void turnOn() {
System.out.println("Light is ON");
}
public void turnOff() {
System.out.println("Light is OFF");
}
}class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
public void execute() {
light.turnOn();
}
}class RemoteControl { // Aufrufer (Invoker)
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void pressButton() {
command.execute();
}
}Der Invoker weiß nicht, welche Aktion ausgeführt wird – er ruft lediglich execute() auf:
Light light = new Light();
Command lightOn = new LightOnCommand(light);
RemoteControl remote = new RemoteControl();
remote.setCommand(lightOn);
remote.pressButton(); // Ausgabe: Licht ist ANDas Command-Pattern zeichnet sich aus, wenn Sie Operationen in eine Warteschlange stellen, eine Undo/Redo-Funktionalität implementieren oder Aufgaben für eine spätere Ausführung planen müssen. Jedes Command kapselt alles Notwendige, um eine Aktion auszuführen, was Ihr System flexibler und erweiterbarer macht.
Aufgabe
EinfachLassen Sie uns ein Smart-Home-Automatisierungssystem mit dem Command Pattern bauen! Sie erstellen ein System, in dem verschiedene Heimgeräte über Befehle gesteuert werden können, was es ermöglicht, Aktionen in eine Warteschlange zu stellen, auszuführen und sogar für die spätere Verwendung zu speichern – genau wie eine Universalfernbedienung, die nicht wissen muss, wie jedes Gerät intern funktioniert.
Sie werden Ihren Code über vier Dateien organisieren:
Command.java: Definieren Sie dasCommand-Interface, das alle Gerätebefehle implementieren werden. Es sollte eine einzige Methodeexecute()deklarieren, die die Aktion des Befehls ausführt.Devices.java: Erstellen Sie zwei Receiver-Klassen, die Smart-Home-Geräte repräsentieren:Television- hat die MethodenpowerOn(), dieTV is now ONausgibt,powerOff(), dieTV is now OFFausgibt, undsetChannel(int channel), dieTV channel set to [channel]ausgibt.Thermostat- hat die MethodensetTemperature(int temp), dieThermostat set to [temp] degreesausgibt, undturnOff(), dieThermostat turned OFFausgibt.Commands.java: Erstellen Sie konkrete Befehlsklassen, die die Geräteoperationen kapseln:TVOnCommand- nimmt einTelevision-Objekt in seinem Konstruktor entgegen und ruft bei der AusführungpowerOn()auf.TVOffCommand- nimmt einTelevision-Objekt in seinem Konstruktor entgegen und ruft bei der AusführungpowerOff()auf.TVChannelCommand- nimmt einTelevision-Objekt und einint channelin seinem Konstruktor entgegen und ruft bei der AusführungsetChannel()mit diesem Kanal auf.ThermostatSetCommand- nimmt einThermostat-Objekt und einint temperaturein seinem Konstruktor entgegen und ruft bei der AusführungsetTemperature()mit diesem Wert auf.Main.java: Erstellen Sie eineRemoteControl-Klasse (den Invoker), die eine MethodesetCommand(Command cmd)zum Speichern eines Befehls undpressButton()zum Ausführen des gespeicherten Befehls hat.In Ihrer Main-Methode erhalten Sie zwei Eingaben: eine TV-Kanalnummer (Integer) und eine Thermostattemperatur (Integer).
Erstellen Sie ein
Television-Objekt und einThermostat-Objekt. Erstellen Sie eineRemoteControlund demonstrieren Sie das Command Pattern durch:- Setzen eines
TVOnCommandund Drücken der Taste - Setzen eines
TVChannelCommandmit Ihrem eingegebenen Kanal und Drücken der Taste - Setzen eines
ThermostatSetCommandmit Ihrer eingegebenen Temperatur und Drücken der Taste - Setzen eines
TVOffCommandund Drücken der Taste
- Setzen eines
Sie erhalten zwei Eingaben in dieser Reihenfolge: den TV-Kanal (Integer) und die Thermostattemperatur (Integer).
Zum Beispiel wäre bei den Eingaben 5 und 72 Ihre Ausgabe:
TV is now ON
TV channel set to 5
Thermostat set to 72 degrees
TV is now OFFBeachten Sie, dass die RemoteControl nie direkt mit den Geräten interagiert – sie kennt nur das Command-Interface. Jeder Befehl kapselt alles, was zur Ausführung seiner Aktion erforderlich ist, was es einfach macht, neue Geräte und Befehle hinzuzufügen, ohne den Invoker zu ändern!
Spickzettel
Das Command-Muster ist ein Verhaltensmuster, das eine Anfrage als Objekt kapselt. Dies ermöglicht es, Clients mit verschiedenen Anfragen zu parametrisieren, Anfragen in Warteschlangen zu stellen oder zu protokollieren und rückgängig machbare Operationen zu unterstützen.
Das Muster entkoppelt das Objekt, das die Operation aufruft (Invoker), von demjenigen, das sie ausführt (Receiver).
Kernkomponenten
- Command: Interface, das die Methode
execute()deklariert - ConcreteCommand: Klassen, die spezifische Aktionen implementieren
- Receiver: Objekt, das die eigentliche Arbeit verrichtet
- Invoker: Objekt, das Befehle auslöst
Grundstruktur
// Command-Interface
interface Command {
void execute();
}
// Receiver
class Light {
public void turnOn() {
System.out.println("Light is ON");
}
public void turnOff() {
System.out.println("Light is OFF");
}
}
// ConcreteCommand
class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
public void execute() {
light.turnOn();
}
}
// Invoker
class RemoteControl {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void pressButton() {
command.execute();
}
}Verwendung
Light light = new Light();
Command lightOn = new LightOnCommand(light);
RemoteControl remote = new RemoteControl();
remote.setCommand(lightOn);
remote.pressButton(); // Ausgabe: Light is ONDer Invoker weiß nicht, welche Aktion ausgeführt wird – er ruft lediglich execute() auf. Dies macht das System flexibel und erweiterbar, ideal für das Warteschlangenmanagement von Operationen, die Implementierung von Rückgängig/Wiederholen-Funktionalitäten oder die Planung von Aufgaben.
Probier es selbst
import java.util.Scanner;
// TODO: Erstelle die Klasse RemoteControl (den Invoker) mit:
// - der Methode setCommand(Command cmd), um einen Befehl zu speichern
// - der Methode pressButton(), um den gespeicherten Befehl auszuführen
class RemoteControl {
// TODO: Implementiere RemoteControl
}
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Eingaben lesen
int channel = scanner.nextInt();
int temperature = scanner.nextInt();
// TODO: Erstelle einen Television und einen Thermostat
// TODO: Erstelle eine RemoteControl
// TODO: Demonstriere das Command Pattern:
// 1. Setze einen TVOnCommand und drücke den Knopf
// 2. Setze einen TVChannelCommand mit dem eingegebenen Kanal und drücke den Knopf
// 3. Setze einen ThermostatSetCommand mit der eingegebenen Temperatur und drücke den Knopf
// 4. Setze einen TVOffCommand und drücke den Knopf
scanner.close();
}
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienEinführung in die OOPKlassen vs. ObjekteDas this-SchlüsselwortMethodenFelder (Attribute)Konstruktor-MethodeKonstruktor-ÜberladungRückblick – Einfacher Taschenrechner4Vererbung
Grundlagen der Vererbung (extends)Das super-SchlüsselwortMethoden überschreiben (@Override)Konstruktor-VerkettungDie Object-KlasseEinfache & mehrstufige VererbungWarum keine Mehrfachvererbung von KlassenZusammenfassung - Mitarbeiter-Hierarchie7Spezielle Methoden & Object-Klasse
toString()-Methodeequals() und hashCode()clone()-MethodecompareTo() und ComparableComparator-InterfaceZusammenfassung – Benutzerdefiniertes Sortieren2Zugriffsmodifikatoren & Kapselung
Überblick über ZugriffsebenenGetter- und Setter-MethodenInformation HidingDas final-SchlüsselwortRückblick – Bankkonto-Manager5Polymorphismus
Grundlagen der MethodenüberladungMethodenüberschreibung (Laufzeit)Upcasting und DowncastingDer instanceof-OperatorAbstrakte Klassen und MethodenZusammenfassung – Formenrechner8Fortgeschrittene OOP-Konzepte
Komposition vs. VererbungAggregation vs. KompositionInnere, geschachtelte & anonyme KlassenEnums und Enum-MethodenRecords (Java 16+)Sealed Classes (Java 17+)11Design Patterns Teil 1
Einführung in Design PatternsSingleton-PatternFactory-PatternBuilder-PatternObserver-PatternStrategy-Pattern14Abschlussherausforderungen
E-Learning-PlattformBanksystemSpielcharakter-EntwicklungFahrzeugvermietung3Klasseneigenschaften & Statische Member
Instanz- vs. statische VariablenStatische MethodenStatische BlöckeKonstanten (static final)Zusammenfassung – Zähler & Utility6Interfaces & Abstrakte Klassen
Einführung in InterfacesInterfaces implementierenImplementierung mehrerer InterfacesDefault- & statische Methoden in InterfacesAbstrakte Klassen vs. InterfacesFunktionale InterfacesZusammenfassung - Zahlungssystem9Generics
Einführung in GenericsGenerische KlassenGenerische MethodenEingeschränkte TypparameterWildcards (?, extends, super)Zusammenfassung – Generische Container12Entwurfsmuster Teil 2
Command-MusterAdapter-MusterDecorator-MusterTemplate-Method-MusterState-MusterComposite-MusterIterator-Muster