Command-Muster
Teil des Abschnitts Objektorientierte Programmierung der C#-Journey von Coddy — Lektion 56 von 70.
Das Command-Muster 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, sie in eine Warteschlange zu stellen, sie zu protokollieren oder rückgängig machbare Operationen zu unterstützen.
Das Muster trennt das Objekt, das eine Operation aufruft, von dem Objekt, das weiß, wie diese ausgeführt wird. Es besteht aus vier Schlüsselkomponenten: einer Befehlsschnittstelle (Command Interface), konkreten Befehlen (Concrete Commands), einem Empfänger (Receiver – das Objekt, das die eigentliche Arbeit verrichtet) und einem Aufrufer (Invoker – das Objekt, das die Befehle auslöst).
public interface ICommand
{
void Execute();
}
public class Light // Empfänger
{
public void TurnOn() => Console.WriteLine("Light is ON");
public void TurnOff() => Console.WriteLine("Light is OFF");
}
public class TurnOnCommand : ICommand
{
private Light _light;
public TurnOnCommand(Light light) => _light = light;
public void Execute() => _light.TurnOn();
}
public class TurnOffCommand : ICommand
{
private Light _light;
public TurnOffCommand(Light light) => _light = light;
public void Execute() => _light.TurnOff();
}Der Invoker speichert und führt Befehle aus, ohne zu wissen, was sie tun:
public class RemoteControl // Invoker
{
private ICommand _command;
public void SetCommand(ICommand command) => _command = command;
public void PressButton() => _command.Execute();
}
// Verwendung
var light = new Light();
var remote = new RemoteControl();
remote.SetCommand(new TurnOnCommand(light));
remote.PressButton(); // Licht ist AN
remote.SetCommand(new TurnOffCommand(light));
remote.PressButton(); // Licht ist AUSDas Command-Pattern ist besonders leistungsfähig für die Implementierung von Rückgängig-Funktionen, Makro-Aufzeichnungen oder das Einreihen von Operationen in Warteschlangen für die spätere Ausführung. Jeder Befehl kapselt alles ein, was zur Ausführung einer Aktion benötigt wird, was Ihr System flexibler und erweiterbarer macht.
Aufgabe
EinfachLassen Sie uns ein Texteditor-Befehlssystem mit dem Command-Pattern erstellen. Sie werden ein System entwickeln, in dem Bearbeitungsvorgänge wie das Einfügen und Löschen von Text als Befehlsobjekte gekapselt sind, sodass der Editor diese über eine einheitliche Schnittstelle ausführen kann.
Sie werden Ihren Code über drei Dateien organisieren:
Command.cs: Definieren Sie einICommand-Interface im NamespaceTextEditormit einer einzigen MethodeExecute(). Erstellen Sie dann zwei konkrete Befehlsklassen:InsertCommand- nimmt einDocumentund einen Stringtextin seinem Konstruktor entgegen. Wenn es ausgeführt wird, fügt es den Text an das Dokument an und gibtInserted: {text}aus.DeleteCommand- nimmt einDocumentund einint countin seinem Konstruktor entgegen. Wenn es ausgeführt wird, entfernt es die letztencountZeichen aus dem Dokument und gibtDeleted: {count} charactersaus.
Document.cs: Erstellen Sie eineDocument-Klasse im selben Namespace. Dies ist Ihr Empfänger (Receiver) – das Objekt, das die Textoperationen tatsächlich ausführt. Sie sollte Folgendes enthalten:- Ein privates String-Feld zum Speichern des Inhalts (initialisiert als leerer String)
- Eine
Append(string text)-Methode, die Text zum Inhalt hinzufügt - Eine
RemoveLast(int count)-Methode, die die letztencountZeichen entfernt (wenn count die Inhaltslänge überschreitet, wird der gesamte Inhalt gelöscht) - Eine
GetContent()-Methode, die den aktuellen Inhalt zurückgibt
Program.cs: Erstellen Sie eineEditor-Klasse, die als Aufrufer (Invoker) fungiert. Sie sollte eineSetCommand(ICommand command)-Methode und eineExecuteCommand()-Methode haben, die den aktuellen Befehl ausführt. Demonstrieren Sie in Ihrem Hauptcode das Command-Pattern, indem Sie eine Reihe von Bearbeitungsvorgängen verarbeiten.
Sie erhalten die folgenden Eingaben:
- Anzahl der auszuführenden Befehle
- Für jeden Befehl: den Befehlstyp (
insertoderdelete), gefolgt von entweder dem einzufügenden Text oder der Anzahl der zu löschenden Zeichen
Geben Sie nach der Ausführung aller Befehle den endgültigen Dokumentinhalt in einer neuen Zeile im Format Content: {content} aus.
Wenn die Eingaben beispielsweise wie folgt lauten:
4
insert
Hello
insert
World
delete
3
insert
!Sollte die Ausgabe wie folgt aussehen:
Inserted: Hello
Inserted: World
Deleted: 3 characters
Inserted: !
Content: Hello Wo!Beachten Sie, dass der Editor (Invoker) nichts über die Textmanipulation weiß – er führt einfach den Befehl aus, den er erhält. Die Befehle kapseln sowohl die Aktion als auch den Empfänger, was es einfach macht, neue Operationen wie einen ReplaceCommand hinzuzufügen, ohne den Editor zu ändern!
Spickzettel
Das Command-Pattern (Befehlsmuster) ist ein Verhaltensmuster, das eine Anfrage als eigenständiges Objekt kapselt, welches alle Informationen über die Anfrage enthält. Dies ermöglicht es Ihnen, Anfragen als Methodenargumente zu übergeben, sie in eine Warteschlange zu stellen, zu protokollieren oder rückgängig machbare Operationen zu unterstützen.
Das Muster besteht aus vier Schlüsselkomponenten:
- Command-Interface - definiert die Execute()-Methode
- Konkrete Befehle (Concrete commands) - implementieren das Command-Interface und kapseln spezifische Aktionen
- Empfänger (Receiver) - das Objekt, das die eigentliche Arbeit verrichtet
- Aufrufer (Invoker) - das Objekt, das Befehle auslöst, ohne deren Implementierung zu kennen
Command-Interface und konkrete Befehle
public interface ICommand
{
void Execute();
}
public class TurnOnCommand : ICommand
{
private Light _light;
public TurnOnCommand(Light light) => _light = light;
public void Execute() => _light.TurnOn();
}
public class TurnOffCommand : ICommand
{
private Light _light;
public TurnOffCommand(Light light) => _light = light;
public void Execute() => _light.TurnOff();
}Empfänger (Receiver)
public class Light
{
public void TurnOn() => Console.WriteLine("Light is ON");
public void TurnOff() => Console.WriteLine("Light is OFF");
}Aufrufer (Invoker)
public class RemoteControl
{
private ICommand _command;
public void SetCommand(ICommand command) => _command = command;
public void PressButton() => _command.Execute();
}Verwendung
var light = new Light();
var remote = new RemoteControl();
remote.SetCommand(new TurnOnCommand(light));
remote.PressButton(); // Light is ON
remote.SetCommand(new TurnOffCommand(light));
remote.PressButton(); // Light is OFFDas Command-Pattern ist nützlich für die Implementierung von Rückgängig-Funktionen, Makro-Aufzeichnungen oder das Einreihen von Operationen in Warteschlangen für eine spätere Ausführung. Jeder Befehl kapselt alles, was zur Ausführung einer Aktion benötigt wird, was Systeme flexibler und erweiterbarer macht.
Probier es selbst
using System;
using TextEditor;
// TODO: Erstelle die Editor-Klasse (den Invoker)
public class Editor
{
// TODO: Füge ein privates Feld hinzu, um den aktuellen Befehl zu speichern
private ICommand _command;
// TODO: Implementiere die SetCommand-Methode
public void SetCommand(ICommand command)
{
// TODO: Speichere den Befehl
}
// TODO: Implementiere die ExecuteCommand-Methode
public void ExecuteCommand()
{
// TODO: Führe den aktuellen Befehl aus
}
}
class Program
{
public static void Main(string[] args)
{
// Lies die Anzahl der Befehle ein
int n = Convert.ToInt32(Console.ReadLine());
// Erstelle das Dokument (Receiver)
Document document = new Document();
// Erstelle den Editor (Invoker)
Editor editor = new Editor();
// TODO: Verarbeite jeden Befehl
for (int i = 0; i < n; i++)
{
string commandType = Console.ReadLine();
// TODO: Erstelle basierend auf commandType den entsprechenden Befehl
// - Wenn "insert": Lies den Text und erstelle einen InsertCommand
// - Wenn "delete": Lies die Anzahl und erstelle einen DeleteCommand
// Setze dann den Befehl im Editor und führe ihn aus
}
// TODO: Gib den finalen Inhalt im Format "Content: {content}" aus
}
}
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 DateienNamespaces & DirektivenEinführung in Klassen & ObjekteDas 'this'-SchlüsselwortMethoden und ParameterFelder vs. EigenschaftenKonstruktorenObjekt-InitialisiererZusammenfassung - Einfacher Taschenrechner4Vererbung
Grundlagen der Vererbung (:) SyntaxDas 'base'-SchlüsselwortVirtual & Override SchlüsselwörterVersiegelte KlassenDie 'object'-BasisklasseZusammenfassung – Mitarbeiter-Hierarchie7Fortgeschrittene Funktionen
OperatorüberladungIndexer (this[])ToString() überschreibenErweiterungsmethodenZusammenfassung - Benutzerdefinierte Liste2Eigenschaften & Statische Member
Automatisch implementierte EigenschaftenRead-Only- & Write-Only-EigenschaftenStatische Felder & MethodenStatische KlassenExpression-Bodied Member5Polymorphismus & Interfaces
Compile- vs. Runtime-PolymorphismusInterface vs. abstrakte KlasseMehrfache InterfacesExplizite InterfacesUpcasting & DowncastingRecap – Shape Calculator8Fortgeschrittene OOP-Konzepte
Komposition statt VererbungGenerics (Klassen & Methoden)Delegates und EventsAttribute und ReflectionIDisposable & using-AnweisungDependency Injection Grundlagen11Entwurfsmuster Teil 2
Command-MusterAdapter-MusterDecorator-MusterTemplate-Method-MusterZustandsmusterKompositum-Muster3Klassenarchitektur
Instanz- vs. statische Daten'readonly' & 'const' SchlüsselwörterBacking FieldsZusammenfassung - Bankkonto-Manager6Kapselung
ZugriffsmodifikatorenProperties zur KapselungImplementierung von Data HidingImmutability-MusterRückblick – Studierendendaten