Menu
Coddy logo textTech

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 AUS

Das 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.

challenge icon

Aufgabe

Einfach

Lassen 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 ein ICommand-Interface im Namespace TextEditor mit einer einzigen Methode Execute(). Erstellen Sie dann zwei konkrete Befehlsklassen:
    • InsertCommand - nimmt ein Document und einen String text in seinem Konstruktor entgegen. Wenn es ausgeführt wird, fügt es den Text an das Dokument an und gibt Inserted: {text} aus.
    • DeleteCommand - nimmt ein Document und ein int count in seinem Konstruktor entgegen. Wenn es ausgeführt wird, entfernt es die letzten count Zeichen aus dem Dokument und gibt Deleted: {count} characters aus.
  • Document.cs: Erstellen Sie eine Document-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 letzten count Zeichen 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 eine Editor-Klasse, die als Aufrufer (Invoker) fungiert. Sie sollte eine SetCommand(ICommand command)-Methode und eine ExecuteCommand()-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 (insert oder delete), 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 OFF

Das 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
    }
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung