Befehlsmuster
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. Durch diese Umwandlung kannst du Anfragen als Methodenargumente übergeben, sie in eine Warteschlange stellen, protokollieren oder rückgängig machbare Vorgänge unterstützen.
Das Muster trennt das Objekt, das eine Operation aufruft, von dem Objekt, das weiß, wie sie ausgeführt wird. Es besteht aus vier wesentlichen Komponenten: einer Befehlsschnittstelle, konkreten Befehlen, einem Empfänger (dem Objekt, das die eigentliche Arbeit ausführt) und einem Aufrufer (dem Objekt, das 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 Aufrufer speichert Befehle und führt sie aus, ohne zu wissen, was sie tun:
public class RemoteControl // Aufrufer
{
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 ist AN
remote.SetCommand(new TurnOffCommand(light));
remote.PressButton(); // Light ist AUSDas Command-Muster ist besonders leistungsfähig für die Implementierung der Rückgängig-Funktion, die Aufzeichnung von Makros oder das Einreihen von Vorgängen zur späteren Ausführung. Jeder Befehl kapselt alles, was zum Ausführen einer Aktion erforderlich ist, wodurch dein System flexibler und erweiterbarer wird.
Aufgabe
EinfachErstellen wir ein Texteditor-Befehlssystem mithilfe des Command-Musters. Du erstellst ein System, in dem Bearbeitungsvorgänge wie das Einfügen und Löschen von Text als Befehlsobjekte gekapselt werden, sodass der Editor sie über eine einheitliche Schnittstelle ausführen kann.
Du organisierst deinen Code über drei Dateien:
Command.cs: Definiere eineICommand-Schnittstelle im NamespaceTextEditormit einer einzigen MethodeExecute(). Erstelle anschließend zwei konkrete Befehlsklassen:InsertCommand– nimmt in seinem Konstruktor einDocumentund einen Stringtextentgegen. Bei der Ausführung hängt er den Text an das Dokument an und gibtInserted: {text}aus.DeleteCommand– nimmt in seinem Konstruktor einDocumentund eineint countentgegen. Bei der Ausführung entfernt er die letztencountZeichen aus dem Dokument und gibtDeleted: {count} charactersaus.
Document.cs: Erstelle eineDocument-Klasse im selben Namespace. Dies ist dein Empfänger – das Objekt, das die Textoperationen tatsächlich ausführt. Es sollte Folgendes enthalten:- Ein privates String-Feld zum Speichern des Inhalts (mit einer leeren Zeichenfolge initialisieren)
- Eine
Append(string text)-Methode, die Text an den Inhalt anhängt - Eine
RemoveLast(int count)-Methode, die die letztencountZeichen entfernt (wenn count die Inhaltslänge überschreitet, den gesamten Inhalt löschen) - Eine
GetContent()-Methode, die den aktuellen Inhalt zurückgibt
Program.cs: Erstelle eineEditor-Klasse, die als Aufrufer fungiert. Sie sollte eineSetCommand(ICommand command)-Methode und eineExecuteCommand()-Methode besitzen, die den aktuellen Befehl ausführt. Zeige in deinem Hauptcode das Command-Muster, indem du eine Reihe von Bearbeitungsvorgängen verarbeitest.
Du erhältst die folgenden Eingaben:
- Anzahl der auszuführenden Befehle
- Für jeden Befehl: der Befehlstyp (
insertoderdelete), gefolgt von dem einzufügenden Text oder der Anzahl der zu löschenden Zeichen
Nachdem alle Befehle ausgeführt wurden, gib den endgültigen Dokumentinhalt in einer neuen Zeile im Format Content: {content} aus.
Wenn die Eingaben beispielsweise folgendermaßen lauten:
4
insert
Hello
insert
World
delete
3
insert
!Die Ausgabe sollte folgendermaßen aussehen:
Inserted: Hello
Inserted: World
Deleted: 3 characters
Inserted: !
Content: Hello Wo!Beachte, dass der Editor (Aufrufer) nichts über die Textmanipulation weiß – er führt einfach den Befehl aus, der ihm übergeben wird. Die Befehle kapseln sowohl die Aktion als auch den Empfänger, wodurch sich problemlos neue Operationen wie ReplaceCommand hinzufügen lassen, ohne den Editor zu ändern!
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. EigenschaftenKonstruktorenObjektinitialisiererWiederholung – Einfacher Taschenrechner4Vererbung
Grundlegende Vererbungs-(:)-SyntaxDas „base“-Schlüsselwort„virtual“- und „override“-SchlüsselwörterVersiegelte KlassenDie „object“-BasisklasseWiederholung – Mitarbeiterhierarchie7Erweiterte Funktionen
Überladen von OperatorenIndexer (this[])ToString()-ÜberschreibungErweiterungsmethodenRückblick – Benutzerdefinierte Liste10Entwurfsmuster Teil 1
Einführung in EntwurfsmusterThread-sicheres SingletonFactory-MusterObserver-Muster (Events)Strategy-Muster13Abschließende Herausforderungen
E-Learning-PlattformKomponente für SpielfigurenFahrzeugvermietungsdienst2Eigenschaften & statische Member
Automatisch implementierte EigenschaftenNur lesbare/schreibbare EigenschaftenStatische Felder & MethodenStatische KlassenMember mit Ausdruckskörpern5Polymorphie & Interfaces
Compile- vs. Laufzeit-PolymorphieInterface vs. abstrakte KlasseMehrere InterfacesExplizite InterfacesUpcasting & DowncastingWiederholung – Formenrechner8Fortgeschrittene OOP-Konzepte
Komposition statt VererbungGenerics (Klassen & Methoden)Delegates und EventsAttribute und ReflectionIDisposable & using-AnweisungGrundlagen der Dependency Injection11Entwurfsmuster Teil 2
BefehlsmusterAdaptermusterDekorierermusterVorlagenmethodenmusterZustandsmusterKompositumsmuster3Klassenarchitektur
Instanz- vs. statische DatenSchlüsselwörter 'readonly' & 'const'Backing FieldsRückblick – Bankkontenverwaltung6Kapselung
ZugriffsmodifikatorenEigenschaften zur KapselungImplementierung der DatenkapselungMuster für UnveränderlichkeitRückblick – SchülerdatenÜbe selbstständig: Online-C#-Compiler