This post originated from an RSS feed registered with .NET Buzz
by -.
Original Post: Undo und Redo implementieren
Feed Title: Norbert Eder - Living .NET
Feed URL: http://feeds.feedburner.com/NorbertEder-Livingnet
Feed Description: Copyright (c)2005, 2006 by Norbert Eder
Es kommt ja doch häufig vor, dass eine Anwendung ein Undo bzw. ein Redo unterstützen muss. Wird diese Funktionalität gefordert wird immer wieder nach einer Umsetzungsmöglichkeit gefragt. Hier eine kleine Lösung, die für diese Zwecke verwendet werden kann.
Hinweis: In diesem Beispiel werden keine Fehlerabfragen gemacht. Es empfiehlt sich, einen Ausführungsstatus zu integrieren, der das Verhalten entsprechend beeinflusst.
Design
Um diese Funktionalität umzusetzen, wird das Command-Pattern verwendet. Dieses eignet sich sehr gut dazu, Aktionen auszuführen und diese auch wieder zurück zu nehmen. Erweitert wird das Command-Pattern um einen CommandManager, der für die Ausführung und Verwaltung der einzelnen Commands zuständig ist.
Implementierung
Zuerst wird eine Basisklasse für den Command implementiert, welche die Methoden Execute(), Undo und Redo zur Verfügung stellt.
Als weiteren Schritt wird der CommandManager implementiert. Dieser ist in diesem Fall ein Singleton (damit er nur einmal pro Instanz vorhanden sein kann) und bietet nach aussen hin die selbe Funktionalität wie ein Command an. Intern werden die einzelnen Commands jedoch in einem Stack behalten, um den Verlauf der einzelnen Commands nachvollziehen zu können. Darüber kann nun ein Undo bzw. ein Redo abgebildet werden.
Damit ist die eigentliche Implementierung bereits abgeschlossen und wir können zu einem ersten Test schreiten. Hierzu benötigen wir ein paar kleinere Klassen, die nicht wirklich etwas aufregendes machen.
Hier eine Klasse Person, die zwei Eigenschaften zur Verfügung stellt, anhand derer getestet wird. Zudem implementiert sie das Interface ICloneable (wird im nachfolgenden Command benutzt) und überschreibt die ToString-Methode.
publicclass Person : ICloneable
{
#region Properties
public String Firstname { get; set; }
public String Lastname { get; set; }
#endregion Properties
#region ICloneable Members
publicobject Clone()
{
Person p = new Person();
p.Firstname = this.Firstname;
p.Lastname = this.Lastname;
return p;
}
#endregion ICloneable Members
#region Overrides
publicoverridestring ToString()
{
return String.Format("{0}, {1}", Lastname, Firstname);
}
#endregion Overrides
}
Als nächsten Schritt müssen wir noch einen konkreten Command implementieren, der eine Aktion durchführt. Dieser wird sich lediglich ein wenig mit den Eigenschaften der Person spielen:
Wichtig ist an dieser Stelle nur, dass sich der Command den ursprünglichen Status des übergebenen Objektes merkt und somit eine Undo bzw. Redo-Funktionalität anbieten kann.
Schlussendlich ein Stück Code, welches bei mir in einer Konsolen-Anwendung direkt in der static void Main ausgeführt wird:
Und nun möchte ich das Ergebnis nicht vorenthalten:
Fazit
Wie zu sehen ist, ist ein Undo bzw. Redo-Mechanismus sehr einfach zu implementieren. Abhängig der Umgebung in welcher diese Methode zu tragen kommt, müssen eventuell weitere Mechanismen eingefügt werden.