Development:Design UndoRedo
Status: Entwurf zur Diskussion
Grundgedanken
Das UndoRedo-System wird von den Klassen, die speicherbare Member besitzen, transparent intern gelöst. Dabei wird in den entsprechenden Setter-Methoden der Wert nicht mehr direkt gesetzt, sondern stattdessen das nötige UndeRedoObjekt generiert und an den UndoRedoManager übergeben. Dieser führt dann das Kommandoobjekt aus. Die Schnittstelle der Klasse nach außen wird dadurch nicht beeinflusst.
Eine Änderung des Zustandes der Klasse muss dabei immer zu einem konsistenten Datenstand führen. Daher gelten folgende Regeln: 1) Datenduplikation muss vermieden werden 2) Alle Klassen, die von Daten einer anderen Klasse abhängen, müssen bei einer Änderung benachrichtigt werden. Dafür kann z.B. das Beobachtermuster genutzt werden. Auf diese Art müssen z.B. die GUI-Elemente des Editors benachrichtigt werden, aber natürlich auch die eigentliche Simulationslogik u.ä.
Klassenhierarchie
TODO in Klassendiagramm umwandeln
(Beispielhaft: DateiVerwaltung)
enthält genau einen
UndoRedoManager
enthält Liste
UndoRedoObjekt
ist Basisklasse von
UndoRedoGeneric<typedef TypeData, TODO Funktionen die Verhalten spezifizieren>
UndoRedo{SpeziellerName}
TODO Präfix UndoRedo so in Ordnung oder lieber "Command" o.ä.?
Klassen
UndoRedoManager
Konstruktor
Nimmt entgegen: 1) Wert, der gesetzt werden soll 2) Objekt, bei dem der Wert gesetzt werden soll 3) Do-Methode des Objekts, bei dem der Wert gesetzt werden soll 4) Undo-Methode des Objekts, bei dem der Wert gesetzt werden soll
Methode bool hasChangesSinceLastSafe
return lastSafe!=actualState
Methode redo()
++actualState; actualState->do(); emitHasChanged();
Methode undo()
actualState->undo(); --actualState;
Methode appendCommand(UndoRedoObject* command)
list.delete(actualState+1, list.end(); if (!actualState->tryAppendAndRun(command))
list.append(command); command->do();
actualState=list.end()-1;
Methode fileHasBeenSaved()
lastSafe=actualState;
Private Methode emitHasChanged()
if (lastHasChanged!=hasChangesSinceLastSave)
lastHasChanged=hasChangesSinceLastSave; emit hasChangesChanged(lastHasChanged);
Signal hasChangesChanged(bool fileIsChanged)
Member list
Liste der Undo-Redo-Objekte
Member lastSafe
Iterator auf den Listeneintrag, der dem letzten Speichern entspricht
Member actualState
Iterator auf den Listeneintrag, der gerade aktuell ist
UndoRedoObjekt
Methode Do
Führt den Befehl aus, speichert den alten Wert als Vorbereitung für undo()
Methode Undo
Führt den Befehl aus, speichert den alten Wert als Vorbereitung für do() (was in dem Fall ein redo ist)
Methode tryAppendAndRun(UndoRedoObjekt* command)
wenn Typ übereinstimmt, state stimmt und das Kommandoobjekt darauf ausgelegt ist: Führe aktuelle Änderung mit der letzten Änderung zusammen. Beispiel: Ein String wird mehrere Male hintereinander geändert (z.B. jeder Buchstabe löst ein Änderungssignal aus). In dem Fall sollte nicht für jeden Buchstaben ein eigenes Kommandoobjekt erzeugt werden, das macht die Änderungen so unpraktisch...
Member state
Hält die Information, ob das Objekt ausgeführt wurde oder rückgängig gemacht wurde